HTTP security headers are response headers your web server sends to browsers. They tell the browser how to behave with your content — whether to allow it in an iframe, how to handle mixed content, which origins can load resources, and more. Getting them right is a one-time effort. The problem is keeping them right.
A deployment can silently remove a header. A CDN configuration change can override your headers. A new nginx config can reset everything to defaults. Without monitoring, you find out weeks later — usually from a security scanner or a bug bounty report.
Tells browsers to only connect via HTTPS, even if the user types http://. Prevents SSL stripping attacks.
Recommended value:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000 = 1 year. includeSubDomains covers subdomains. preload adds you to browsers' hardcoded HTTPS list (submit at hstspreload.org).
Add in nginx: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Controls which resources (scripts, styles, images) the browser is allowed to load. Prevents cross-site scripting (XSS) by blocking inline scripts and untrusted origins.
CSP is the most complex header to configure correctly. A simple starting point:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
For SPAs with third-party scripts, you'll need to whitelist each source explicitly. Use Content-Security-Policy-Report-Only first to test without blocking.
Prevents your page from being embedded in an <iframe> on another site — the primary defense against clickjacking attacks.
X-Frame-Options: SAMEORIGIN
SAMEORIGIN allows your own domain to embed the page. DENY blocks all framing. Note: CSP's frame-ancestors directive is the modern replacement, but X-Frame-Options provides broader browser support.
Prevents browsers from MIME-sniffing a response away from the declared content type. Stops a class of attack where a file uploaded as an image gets executed as JavaScript.
X-Content-Type-Options: nosniff
This one is trivially easy to add and has essentially no downside. If this header is missing from your site, it's a free fix.
Controls how much referrer information is included in requests. Without this header, navigating from your site to an external link sends your full URL as the referrer — which can leak sensitive URL parameters (tokens, IDs, query strings).
Referrer-Policy: strict-origin-when-cross-origin
This sends the origin (no path) for cross-origin requests and the full URL for same-origin requests. A safe default for most sites.
Formerly Feature-Policy. Controls which browser features your page can use — camera, microphone, geolocation, etc. Limits the blast radius if a malicious third-party script runs on your site.
Permissions-Policy: camera=(), microphone=(), geolocation=()
The empty parentheses disable each feature for all origins including your own. Adjust based on your actual feature usage.
server {
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# CSP — customize for your app
add_header Content-Security-Policy "default-src 'self'" always;
}
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>
import helmet from 'helmet';
app.use(helmet());
// Helmet sets sensible defaults for all major security headers
// Customize specific headers:
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
}
}));
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "SAMEORIGIN" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains" }
]
}
]
}
| Header | Missing grade | Present grade | Fix time |
|---|---|---|---|
| HSTS | F | A (with includeSubDomains) | 5 min |
| CSP | F | A (non-trivial) / B (basic) | 30 min+ |
| X-Frame-Options | F | A (DENY or SAMEORIGIN) | 2 min |
| X-Content-Type-Options | F | A (nosniff) | 2 min |
| Referrer-Policy | F | A (strict-origin) | 2 min |
| Permissions-Policy | F | A (any value) | 5 min |
Headers are easy to set and just as easy to accidentally remove. Common regression scenarios:
External header monitoring catches regressions within minutes. The alert goes to email or Slack; the engineer fixes the config and redeploys; the headers are back before users are exposed for any meaningful period.
HeaderGuard monitors your site's HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy. Get an email the moment any header changes value, disappears, or reappears. $9/month for up to 10 domains.
Join the HeaderGuard waitlist → Check your headers free← All articles · HeaderGuard · Free security headers checker