How to Check Security Headers (and Why They Matter)
Security headers are one of the lowest-effort, highest-impact improvements you can make to any production site.
Security headers are HTTP response headers that instruct browsers how to behave when handling your site's content. Adding them is a matter of a few lines in your server config, yet a surprisingly large number of production websites are missing them entirely.
This guide covers the headers that matter most, what they do, and exactly how to set them up on the most common platforms.
Why Security Headers Matter
Without security headers, your site is vulnerable to attacks that the browser would otherwise prevent. Cross-site scripting (XSS), clickjacking, and protocol downgrade attacks can all be mitigated with the right headers in place. A properly configured Content-Security-Policy header can stop the majority of XSS attacks.
Security headers also appear in security scanner ratings from tools like Mozilla Observatory and Qualys SSL Labs. If you handle user data, missing headers can become a compliance concern.
You can instantly check which headers your site is serving using the AuditZap security headers checker.
The Headers You Need
Strict-Transport-Security (HSTS)
HSTS tells browsers that your site should only ever be accessed over HTTPS. Once a browser has seen this header, it will refuse to connect over HTTP for the duration specified in max-age — even if the user types http:// in the address bar.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadThe max-age is in seconds — 31536000 is one year. Before setting a long max-age, make sure every page of your site is accessible over HTTPS, including all subdomains if you use includeSubDomains.
X-Content-Type-Options
This header prevents browsers from MIME-sniffing — guessing a file's content type based on its contents rather than the Content-Type header. MIME-sniffing can be exploited to make browsers execute files as a different type than intended.
X-Content-Type-Options: nosniffThe only valid value is nosniff. Always include it.
X-Frame-Options
Clickjacking attacks embed your site in an invisible <iframe> on a malicious page. Users end up clicking on your site — submitting forms, making purchases, granting permissions — without realising it.
X-Frame-Options: DENYDENY prevents your site from being framed anywhere. SAMEORIGIN allows it to be framed only by pages on the same domain.
Content-Security-Policy (CSP)
CSP is the most powerful — and most complex — security header. It lets you declare exactly which sources of scripts, styles, and images the browser should load. A strict CSP can prevent XSS attacks even if an attacker successfully injects a script tag.
A reasonable starting CSP for a mostly-static site:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'CSP policies can break sites if you load resources from third-party domains. Build your policy incrementally using Content-Security-Policy-Report-Only mode first, which logs violations without blocking anything.
Referrer-Policy
Controls how much referrer information is sent when users navigate away from your site. The default browser behaviour leaks full URLs to third-party sites — including query strings that may contain user IDs or session tokens.
Referrer-Policy: strict-origin-when-cross-originThis sends the full URL for same-origin requests, and only the origin (no path or query string) for cross-origin requests.
Permissions-Policy
This header lets you control which browser APIs your pages can use — geolocation, camera, microphone, payment, etc. Restricting these reduces your attack surface.
Permissions-Policy: geolocation=(), microphone=(), camera=()An empty () value means no one — including your own code — can use that feature.
How to Add Security Headers
Nginx
Add to your server block or a specific location block:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;The always parameter ensures the header is sent even for error responses.
Apache
Add to your .htaccess or httpd.conf:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "DENY"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"Ensure mod_headers is enabled: a2enmod headers.
Vercel
Add to vercel.json:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains; preload" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "geolocation=(), microphone=(), camera=()" }
]
}
]
}Next.js projects can alternatively use the headers() function in next.config.js.
Cloudflare
If your site sits behind Cloudflare, add security headers using a Transform Rule:
- Go to Rules > Transform Rules > Modify Response Header
- Create a rule matching all requests (
true) - Add each header as a “Set” action
This works regardless of your origin server and handles all traffic passing through Cloudflare.
How to Test Your Headers
After deploying, verify your headers are working:
- AuditZap security headers checker — instant free check, shows what is present and what is missing
- curl —
curl -I https://yoursite.comshows response headers in the terminal - Browser DevTools — Network tab, click any request, view the Response Headers section
Run the check after every deployment — it is easy to accidentally remove headers when updating server configs or switching hosting providers.