The Complete Guide to HTTP Security Headers in 2026
APR 15, 2024- Written by
Yves SoeteBlacksight LLC visit us to use our free website security scanner onscanner.blacksight.io
Get notified when new articles drop — visitblacksight.io/blog
to subscribe.
HTTP security headers are the single cheapest defense you can deploy on any website. They cost zero performance overhead, require no code changes to your application, and protect against entire classes of attacks. Yet when I scan thousands of sites through BlackSight, the majority are missing at least two critical headers. Some ship none at all.
The problem is not awareness — most developers have heard of Content-Security-Policy. The problem is getting the values right. A misconfigured CSP is worse than no CSP because it gives you a false sense of security while still allowing injection. This guide covers each header you should set in 2026, with correct values, common mistakes, and how to verify everything works.
1. Content-Security-Policy (CSP)
CSP is the most powerful security header available and the most commonly misconfigured. It tells the browser which sources of content are allowed to load on your page. If an attacker injects a script tag pointing to their server, a proper CSP will block it from executing.
A solid starting point looks like this:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
The most common mistake I see is using unsafe-inline
and unsafe-eval
in script-src. This effectively disables CSP protection against XSS because inline scripts are exactly what attackers inject. If you need inline scripts, use nonce-based or hash-based CSP instead. Generate a random nonce per request, pass it to your script tags, and include it in your policy.
Another frequent error is setting CSP as
Content-Security-Policy-Report-Only
in production and forgetting to switch it to enforcement mode. Report-Only mode logs violations but does not block anything. Use it during testing, then enforce.
If you use third-party services like Google Analytics, Stripe, or a CDN, you will need to whitelist their specific domains. Never use wildcard (*) in script-src — it defeats the entire purpose.
2. Strict-Transport-Security (HSTS)
HSTS tells browsers to only connect to your site over HTTPS, even if the user types http:// or clicks an HTTP link. Without it, the first request to your site can be intercepted by a man-in-the-middle attacker who downgrades the connection before your redirect fires.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Set max-age to at least two years (63072000 seconds). Include subdomains if all your subdomains support HTTPS — this is critical because attackers can set cookies from an insecure subdomain to attack your main domain. The preload directive lets you submit your domain to the HSTS preload list built into browsers, which eliminates even the first-visit vulnerability.
The mistake to avoid: setting a short max-age like 86400 (one day). If a user does not visit your site for two days, HSTS expires and they are vulnerable to downgrade attacks again. Go long or go home.
3. X-Content-Type-Options
This header has exactly one valid value and there is no reason not to set it:
X-Content-Type-Options: nosniff
Without this header, browsers will try to guess (sniff) the MIME type of a response. An attacker can upload a file with a .jpg extension that contains JavaScript, and the browser might execute it as a script if the content looks like JavaScript. The nosniff directive forces the browser to respect the declared Content-Type header and refuse to execute anything that does not match.
This is a one-line header with zero configuration. If your site does not have it, add it now — there is no legitimate reason to omit it.
4. X-Frame-Options
X-Frame-Options prevents your site from being embedded in an iframe on another domain, which is the primary defense against clickjacking attacks. An attacker loads your site in a transparent iframe, overlays it with a fake UI, and tricks users into clicking buttons they cannot see.
X-Frame-Options: DENY
Use DENY if your site should never be framed. Use SAMEORIGIN if your own application needs to iframe itself. The ALLOW-FROM directive was never widely supported and has been removed — use CSP frame-ancestors instead for more granular control.
Note that CSP frame-ancestors supersedes X-Frame-Options in modern browsers, but you should still set both for backward compatibility with older browsers that do not support CSP level 2.
5. Referrer-Policy
When a user clicks a link from your site to another, the browser sends a Referer header containing the full URL they came from. This can leak sensitive information — think URLs containing session tokens, search queries, or internal paths.
Referrer-Policy: strict-origin-when-cross-origin
This is the best default for most sites. It sends the full URL for same-origin requests (useful for your own analytics), sends only the origin (domain) for cross-origin HTTPS-to-HTTPS requests, and sends nothing when downgrading from HTTPS to HTTP.
If you handle particularly sensitive data, consider no-referrer
which strips all referrer information. However, this can break analytics and affiliate tracking, so test before deploying.
6. Permissions-Policy
Formerly called Feature-Policy, this header controls which browser features and APIs your site can use. It prevents malicious scripts from accessing the camera, microphone, geolocation, or other sensitive APIs even if they manage to execute on your page.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()
The empty parentheses () mean "deny to everyone including the page itself." Only enable features you actually use. If your site needs geolocation, set geolocation=(self) to restrict it to your own origin.
The interest-cohort=() directive opts out of Google's FLoC (Federated Learning of Cohorts) tracking. While FLoC has been replaced by Topics API, setting this is still good practice and signals to browsers that you do not want your site participating in cohort-based tracking.
A mistake I see often: not setting Permissions-Policy at all. If you embed third-party iframes (ads, widgets, embedded content), those iframes can request camera or microphone access from your users unless you explicitly deny it.
7. X-XSS-Protection (Deprecated — But Understand Why)
You will still see this header recommended in older guides:
X-XSS-Protection: 0
Yes, the correct value is now 0
(disabled). The XSS auditor built into older browsers had serious flaws — it could actually be exploited to create XSS vulnerabilities that would not otherwise exist. Attackers could craft URLs that triggered the auditor to selectively remove legitimate scripts, leaving only the malicious ones. Chrome removed its XSS auditor entirely in version 78, and other browsers have followed.
Set this header to 0 to explicitly disable any lingering XSS auditors. Your actual XSS protection should come from a proper Content-Security-Policy, output encoding in your application code, and input validation. Do not rely on browser-side heuristics.
8. Testing and Verifying Your Headers
Setting headers is only half the job — you need to verify they are actually being sent and parsed correctly. Here is how I approach verification:
Command line check: Use curl to inspect headers directly:
curl -I https://yourdomain.com
Browser DevTools:
Open the Network tab, click any request, and check the Response Headers section. Look for all seven headers listed in this article.
Automated scanning:
Use BlackSight's header scanner at scanner.blacksight.io to get a complete audit of your security headers with specific remediation advice for each missing or misconfigured header.
CSP testing:
When deploying CSP for the first time, start with Content-Security-Policy-Report-Only and a report-uri endpoint to collect violations without blocking anything. Review the reports for a week, adjust your policy to allow legitimate resources, then switch to enforcement mode.
Remember that headers set in your application code can be overridden or stripped by reverse proxies, CDNs, or load balancers. Always test from the outside — what the browser receives is what matters, not what your application sends.
Bonus: Use our free website vulnerability scanner at
scanner.blacksight.io
Liked this article? Get notified when new articles drop! visitblacksight.io/blog
to subscribe