Security headers are HTTP response headers that tell browsers how to handle your content securely. They're your first line of defense against XSS, clickjacking, and other common web attacks—and 98.5% of AI-built applications are missing them.
Why Security Headers Matter
When a browser loads your website, it trusts whatever instructions you give it. Security headers let you tell browsers:
- What content sources are allowed to run (CSP)
- Whether your site can be embedded in frames (X-Frame-Options)
- Whether to always use HTTPS (HSTS)
- How to handle content types (X-Content-Type-Options)
Without these headers, browsers use permissive defaults that make attacks easier.
Content-Security-Policy (CSP)
What it does: Controls which resources (scripts, styles, images, etc.) the browser is allowed to load.
Why it matters: Without CSP, if an attacker injects malicious JavaScript into your page (XSS), the browser will happily execute it. CSP limits what scripts can run, dramatically reducing XSS impact.
Example Configuration
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com; frame-ancestors 'none';
What Each Directive Means
| Directive | Purpose |
|---|---|
default-src | Fallback for all resource types |
script-src | Where JavaScript can be loaded from |
style-src | Where CSS can be loaded from |
img-src | Where images can be loaded from |
connect-src | Where XHR/fetch requests can go |
frame-ancestors | Who can embed your site (clickjacking protection) |
Implementation
Vercel (vercel.json):
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Content-Security-Policy",
"value": "default-src 'self'; script-src 'self'"
}
]
}
]
}Netlify (_headers):
/* Content-Security-Policy: default-src 'self'; script-src 'self'
Strict-Transport-Security (HSTS)
What it does: Tells browsers to always use HTTPS, even if the user types http://.
Why it matters: Prevents man-in-the-middle attacks where an attacker intercepts the initial HTTP request before it redirects to HTTPS.
Example Configuration
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
| Parameter | Meaning |
|---|---|
max-age=31536000 | Browser remembers for 1 year |
includeSubDomains | Applies to all subdomains too |
preload | Eligible for browser preload lists |
Warning: Only enable HSTS after confirming your entire site works on HTTPS. Once set, browsers will refuse HTTP connections for the duration of max-age.
X-Frame-Options
What it does: Controls whether your site can be embedded in iframes.
Why it matters: Prevents clickjacking attacks where an attacker overlays invisible iframes to trick users into clicking things they didn't intend to.
Options
| Value | Meaning |
|---|---|
DENY | Never allow framing (most secure) |
SAMEORIGIN | Only allow framing by same origin |
ALLOW-FROM uri | Allow specific origin (deprecated) |
X-Frame-Options: DENY
Note: CSP's frame-ancestors directive is the modern replacement, but X-Frame-Options provides backward compatibility for older browsers.
X-Content-Type-Options
What it does: Prevents browsers from MIME-sniffing (guessing) content types.
Why it matters: Without this, browsers might execute files as scripts even if they're served with a non-script MIME type, enabling certain attacks.
X-Content-Type-Options: nosniff
This is a simple, no-configuration header. Just add it.
Referrer-Policy
What it does: Controls how much referrer information is sent when navigating away from your site.
Why it matters: URLs can contain sensitive information (session tokens, user IDs). Without this header, that information leaks to external sites.
Recommended Values
| Value | Use Case |
|---|---|
strict-origin-when-cross-origin | Good balance (recommended) |
no-referrer | Maximum privacy, breaks some analytics |
same-origin | Only send referrer to same origin |
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy (formerly Feature-Policy)
What it does: Controls which browser features (camera, microphone, geolocation, etc.) your site can use.
Why it matters: Limits what a compromised page or embedded iframe can access.
Permissions-Policy: camera=(), microphone=(), geolocation=()
The empty parentheses () disable the feature entirely.
Complete Header Set
Here's a production-ready set of security headers for most web applications:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://your-api.com; frame-ancestors 'none' Strict-Transport-Security: max-age=31536000; includeSubDomains X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=()
Testing Your Headers
After adding headers, verify they're working:
- Browser DevTools: Network tab → click request → Headers tab
- Command line:
curl -I https://yoursite.com - SecureStack scan: We check all headers automatically
Check Your Headers Now
Our free scan checks all security headers and tells you exactly what's missing.
Scan Your Site →