Every HTTP response your server sends carries headers — and some of them are instructions that harden the visitor’s browser against attack. Security headers can block script injection, prevent clickjacking, stop MIME sniffing, and strip private information from outbound requests. They cost nothing, require no code changes in most cases, and remain the most underused defense on the modern web. Here are the seven that belong on essentially every site, with examples you can adapt.
Why Security Headers Matter
Browsers enforce these headers on every request, for every visitor, without any JavaScript or plugin involved. A missing header means the browser falls back to its default — permissive — behavior. That makes headers an excellent complement to fixing vulnerabilities in your code: the header blocks the attack even when a bug slips through. They are not a substitute for secure development, but they are reliable defense in depth.
1. Content-Security-Policy
Content-Security-Policy (CSP) is the most powerful header on this list. It declares which sources the browser may load scripts, styles, images, fonts, and connections from — and by default blocks everything else, including inline scripts. Because most cross-site scripting (XSS) payloads rely on injecting script that isn’t supposed to be there, a strict CSP neutralizes the attack even when a flaw exists in the application.
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests
A few notes on this example:
default-src 'self'sets the baseline; individual directives (script, style, img, font, connect) refine it.object-src 'none'andbase-uri 'self'close off two classic injection vectors (legacy plugins and base-tag tricks).frame-ancestorscontrols who may embed your pages in iframes — the modern replacement for X-Frame-Options.- If you must keep some inline scripts, use CSP nonces or hashes rather than
'unsafe-inline', which defeats the point.
Roll CSP out with Content-Security-Policy-Report-Only plus a report-uri or report-to directive, watch the violation reports for a few days, then enforce.
2. Strict-Transport-Security
HTTP Strict Transport Security (HSTS) tells the browser: “for the next max-age seconds, always use HTTPS when talking to this domain — never plain HTTP.” That defeats SSL-stripping attacks where a man-in-the-middle silently downgrades the connection.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
max-age=31536000is one year in seconds. Start shorter (for example, one day) while testing, then extend.includeSubDomainsapplies the rule to every subdomain — only add it once you’re certain all subdomains are HTTPS-ready.preloadsignals eligibility for the browser preload list at hstspreload.org. Once preloaded, the policy is baked into browsers — effectively permanent, so submit only after thorough testing.
3. X-Frame-Options
Clickjacking renders your site inside a transparent iframe and tricks visitors into clicking buttons they can’t see. X-Frame-Options prevents framing by other sites:
X-Frame-Options: SAMEORIGIN
Use DENY if no one may frame your pages, or SAMEORIGIN if your own site embeds itself. This header is legacy — the CSP frame-ancestors directive above is the modern equivalent — but both together give coverage across browser generations.
4. X-Content-Type-Options
Browsers normally “sniff” content to guess its type when the declared Content-Type is missing or wrong — a behavior attackers abuse to smuggle executable content past filters. This header stops the guessing:
X-Content-Type-Options: nosniff
There is exactly one valid value, and there is no downside for a site that serves correct content types. Set it everywhere and forget it.
5. Referrer-Policy
When a visitor clicks a link, the browser sends a Referer header showing the page they came from — and if that URL contains tokens, session fragments, or query parameters, you can leak them to third-party sites. Referrer-Policy caps what gets sent:
Referrer-Policy: strict-origin-when-cross-origin
strict-origin-when-cross-origin sends only the origin (no path, no query string) on cross-site requests, sends nothing on HTTPS→HTTP downgrades, and keeps the full referrer within your own site. It’s the sensible default for most sites; no-referrer is the strictest alternative if you want nothing shared at all.
6. Permissions-Policy
Permissions-Policy decides which browser features — camera, microphone, geolocation, USB, payment APIs, and more — a page or its embedded frames may use. Explicitly denying what you don’t use shrinks the attack surface:
Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=(self), usb=()
The empty list () denies a feature to everyone, including you; (self) allows it for your origin only. Audit the features your site actually needs, allow those for self, and deny the rest — if a third-party script is compromised, it inherits the same restrictions.
7. Cross-Origin-Opener-Policy
Cross-Origin-Opener-Policy (COOP) prevents pages from different origins sharing the same browsing context group — the isolation primitive behind defenses against cross-site leaks and Spectre-class side-channel attacks:
Cross-Origin-Opener-Policy: same-origin
same-origin lets your own origin’s windows cooperate while cutting off cross-origin access. Be cautious with its stricter companions: Cross-Origin-Embedder-Policy: require-corp delivers full cross-origin isolation but breaks loading of third-party resources that haven’t opted in. Adopt COOP first; treat COEP as a deliberate upgrade.
The Seven at a Glance
XSS: CSP · Downgrade attacks: HSTS · Clickjacking: X-Frame-Options / frame-ancestors · MIME sniffing: X-Content-Type-Options · Data leaks: Referrer-Policy · Feature abuse: Permissions-Policy · Cross-origin leaks: COOP
How to Add Headers
Headers are configured where your site is served. Common setups:
nginx — in the server block:
add_header X-Content-Type-Options "nosniff" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Content-Security-Policy "default-src 'self'" always;
Note that add_header inside a location block replaces headers inherited from the parent — either repeat them or centralize the configuration.
Apache — in the virtual host or .htaccess:
Header always set X-Content-Type-Options "nosniff" Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Cloudflare — use a Transform Rule to add or modify response headers, with the rule scoped to your hostname. On Cloudflare Pages and Workers, headers can also ship with the deployment itself.
Testing Your Headers
Verify after every change — headers that fail to apply provide zero protection:
- Use our free headers checker to see which of the seven your site serves and what’s missing.
- Check raw responses with
curl -I https://example.comor the Network tab in your browser’s developer tools. - While rolling out CSP, watch the browser console for violation reports — they tell you exactly what the policy would break.
- Re-test after any server, CDN, or plugin change; header regressions are common and silent.
Conclusion
Security headers are one of the highest-value, lowest-effort improvements available to any website — a few lines of server configuration that blunt whole categories of attack for every visitor. Start with the low-risk four (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy), then add HSTS, then tackle CSP in report-only mode until you’re confident. Test after each step, and keep testing — a header that silently disappears is a protection you no longer have.