HTTP security headers are response headers a web server sends with every page that tell the browser how to behave more safely — which scripts it may run, whether the page can be framed, whether to insist on HTTPS. They are among the cheapest, highest-leverage defences on the web: most are a single line of configuration, and together they neutralise whole classes of attack such as clickjacking, protocol downgrade, MIME-sniffing and a large share of cross-site scripting (XSS) impact.
This guide explains each header that matters in 2026, gives a copy-paste baseline you can deploy today, and covers the mistakes that quietly break them. It is grounded in the relevant IETF RFCs and the OWASP Secure Headers Project, the two reference sources for this topic.
What are HTTP security headers?
Every time a browser requests a page, the server replies with a status line, a body (the HTML) and a set of response headers — key/value metadata about the response. Most headers are mundane (Content-Type, Cache-Control).
A subset of them instruct the browser to enforce specific security behaviours. Because the browser enforces them, a correct header protects every visitor automatically, with no change to your application code.
Critically, these headers are read passively. Anyone — including an attacker, a search engine, or a checker like this one — can see exactly which headers your site returns by making a single ordinary request. There is no “scan” to detect; the configuration is public the moment your site is online. That is also why getting them right is worth the few minutes it takes.
Why do security headers matter?
Headers are a defence-in-depth layer. They do not replace fixing the underlying bug, but they dramatically reduce what an attacker can do when something else goes wrong. Three concrete examples:
Protocol downgrade. Without HSTS, a visitor who types example.com makes a first request over plaintext HTTP. An attacker on the same network can intercept that request before the redirect to HTTPS ever happens (the classic SSL-strip attack). HSTS tells the browser to never use HTTP for your domain again.
Clickjacking. Without a framing policy, an attacker can load your site inside an invisible iframe over their own page and trick a logged-in user into clicking buttons they cannot see. X-Frame-Options and CSP frame-ancestors forbid that.
Cross-site scripting. If an attacker injects a <script> into your page, a Content-Security-Policy that only allows scripts from your own origin can stop that injected script from executing at all — turning a critical XSS into a non-event.
The headers that matter, one by one
At a glance, here is what each header defends against and a sane default value. Each is explained in detail below.
| Header | Protects against | Sane default |
|---|---|---|
| Strict-Transport-Security | Protocol downgrade / SSL-strip | max-age=63072000; includeSubDomains; preload |
| Content-Security-Policy | Cross-site scripting (XSS) | default-src 'self' |
| X-Content-Type-Options | MIME-sniffing | nosniff |
| X-Frame-Options | Clickjacking | DENY |
| Referrer-Policy | URL / token leakage | strict-origin-when-cross-origin |
| Permissions-Policy | Unwanted feature access | geolocation=(), camera=(), microphone=() |
| Cross-Origin-Opener-Policy | Cross-window (Spectre-class) attacks | same-origin |
Strict-Transport-Security (HSTS)
Defined in RFC 6797, HSTS forces the browser to use HTTPS for your domain for a fixed period, eliminating the plaintext-first-request window. Set a long max-age (two years is standard) once you are confident every subdomain serves HTTPS.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadpreload directive opts your domain into a list hard-coded into Chrome, Firefox, Safari and Edge, so even the very first visit is HTTPS. Only add it once includeSubDomains is safe for you — preload removal takes months to propagate.Content-Security-Policy (CSP)
The single most effective header against XSS, and the hardest to deploy. CSP is an allow-list of where scripts, styles, images and other resources may load from (the MDN reference documents every directive). Start strict and widen deliberately:
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'Avoid 'unsafe-inline' for scripts where you can — it disables much of CSP's protection. For apps that need inline scripts, use a per-request nonce or hash instead. Deploy with Content-Security-Policy-Report-Only first to catch breakage without blocking anything.
X-Content-Type-Options
One value, no downside. It stops the browser from second-guessing (“sniffing”) the declared content type, which prevents an uploaded file from being reinterpreted as executable script.
X-Content-Type-Options: nosniffX-Frame-Options & frame-ancestors
Controls whether your pages may be embedded in a frame. The modern mechanism is the CSP frame-ancestors directive; the older X-Frame-Options header is still worth sending for legacy browsers. Use both, kept consistent.
X-Frame-Options: DENYReferrer-Policy
Limits how much of the originating URL is sent to other sites when a user clicks a link or loads a third-party resource — stopping full URLs (which may contain tokens or IDs) from leaking.
Referrer-Policy: strict-origin-when-cross-originPermissions-Policy
Disables powerful browser features your site does not use — camera, microphone, geolocation — so that injected or third-party code cannot quietly access them.
Permissions-Policy: geolocation=(), camera=(), microphone=()Cross-Origin-Opener-Policy (COOP)
Isolates your page into its own browsing-context group, defending against cross-window attacks such as those in the Spectre family. Safe for most sites that do not depend on cross-origin popups.
Cross-Origin-Opener-Policy: same-originWhat is a good baseline set of headers?
For a typical site that serves its own content, this is a sound starting set. Add it incrementally — CSP last and in report-only mode — and re-test after each change.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cross-Origin-Opener-Policy: same-origin
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'Want to see which of these your site already sends?
Run the free Security Headers Checker →What are the most common mistakes?
Setting the header on only one path. Security headers must be sent on every response — including redirects, error pages and assets. A common failure is configuring them on the app but not on the front-end proxy or CDN that actually terminates the connection.
HSTS without full HTTPS. If any subdomain still needs HTTP, do not send includeSubDomains — you will lock visitors out of it. Confirm every host is HTTPS first.
CSP with 'unsafe-inline' everywhere. A policy that allows all inline scripts technically “has a CSP” but provides little XSS protection. Treat removing inline script as the real goal.
Duplicate or conflicting headers. If both your application and your proxy set the same header, browsers may see two values and behave unpredictably. Set each header in exactly one place.
How do I monitor and report CSP violations?
A policy you cannot observe is a policy you cannot tighten. Content-Security-Policy supports a reporting channel that tells you what it would have blocked, which is how you deploy a strict policy without breaking the site. Run it in report-only mode first: the browser enforces nothing but sends a JSON report for every violation, so you can find the legitimate scripts and styles you need to allow before you switch on enforcement.
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpointThe modern delivery mechanism is the Reporting API, configured with a Reporting-Endpoints header that names where reports should be sent; the older report-uri directive is still widely honoured and worth keeping alongside it for coverage.
Whichever you use, route the reports somewhere you will actually read — a collector you own or a third-party service — and review them before promoting a report-only policy to an enforcing one. Treat the first week of reports as a to-do list, not a finished result.
document-uri and blocked-uri fields and look for patterns from your own origins before you widen the policy.Which headers can I stop sending?
Several headers that still appear in older hardening checklists are now deprecated or actively counter-productive. Sending them wastes bytes and, in one case, can introduce a vulnerability.
X-XSS-Protection controlled a legacy browser XSS filter that no major browser ships any more. The filter itself could be abused to create information-leak side channels, so current guidance is to send X-XSS-Protection: 0 (explicitly off) or simply omit it and rely on a real Content-Security-Policy instead.
Expect-CT enforced Certificate Transparency during a transition period that has ended; CT is now mandatory in browsers by default, and the header is deprecated. Drop it.
Feature-Policy was renamed and redesigned as Permissions-Policy. If you still send the old header, migrate to Permissions-Policy — the syntax differs, so do not just rename it. Sending both is harmless but redundant.
Public-Key-Pins (HPKP) is the cautionary tale: it let sites pin specific certificate keys, but a mistake could lock every visitor out of the site for the pin's lifetime. Browsers removed support for it. Never deploy it.
How to check your site
You can read your own headers with a single request — for example curl -I https://example.com — but interpreting which are missing, weak or misconfigured against current best practice is the slow part. The free SecScan Security Headers Checker does exactly that: it reads the headers your homepage already returns, grades each against the OWASP recommendations, and gives you the precise value to add. It is passive — equivalent to a single curl request — and needs no signup.
Once headers are in place, the same configuration feeds into your overall website security score, alongside your TLS configuration and exposed-file checks.