Under attack? Get incident response help

info@angelforce.eu
Web security

Security headers that matter, and the ones to retire

A practical guide to CSP with nonces, HSTS and preload, frame-ancestors, Referrer-Policy, Permissions-Policy and COOP, with examples and what to drop.

Also in:TürkçeDeutschFrançaisEspañolItalianoPolskiNederlandsEesti

Illustration: Security headers that matter, and the ones to retire

HTTP response headers tell the browser how to treat your pages: which scripts may run, whether the site may be framed, and whether it must always load over HTTPS. A handful of them block whole classes of attack at almost no cost. Others are obsolete and only add noise, or even risk.

A baseline to start from

This is a reasonable starting point for an HTML application served only over HTTPS. Do not paste it unchanged. The CSP in particular must match how your pages load scripts, its nonce must be new for every response, and HSTS preload is hard to undo.

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: script-src 'nonce-3f9a1c7e2b' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-site

Content-Security-Policy

CSP limits where scripts and other resources can come from. Its main job is to make cross-site scripting (XSS) much harder to exploit.

The OWASP CSP cheat sheet favours a strict policy based on nonces or hashes over a long list of allowed domains. Allowlist policies that are too broad or too detailed tend to be bypassed. A nonce-based policy looks like this:

Content-Security-Policy: script-src 'nonce-3f9a1c7e2b' 'strict-dynamic'; object-src 'none'; base-uri 'none'

Each script tag then carries the same value, as in <script nonce="3f9a1c7e2b">. The nonce must be random and new for every response; a fixed value defeats the purpose. Generate it in your templating layer. OWASP warns against middleware that adds the nonce to every script tag, because it would also bless injected scripts.

If your pages are static, use hashes instead: 'sha256-...' values for each inline script. Any change to the script, even whitespace, changes the hash.

Avoid 'unsafe-inline' and 'unsafe-eval' in script-src. They re-open exactly what CSP is meant to close.

To roll out without breaking things, send the policy as Content-Security-Policy-Report-Only first. Browsers report violations without blocking anything. Fix what the reports show, then switch to enforcement.

Trusted Types

Trusted Types target DOM-based XSS, where your own JavaScript passes attacker-controlled strings to dangerous APIs such as innerHTML, eval() or a script element's src. You enable them with CSP:

Content-Security-Policy: require-trusted-types-for 'script'; trusted-types app-policy

With enforcement on, passing a plain string to one of these sinks throws an error. Code must pass values created by a named policy, usually one that sanitises HTML. MDN lists the API as Baseline since February 2026, meaning it works in current versions of the major browsers. Start in report-only mode, since existing code may write strings straight into the DOM.

Strict-Transport-Security and preload

HSTS tells browsers to connect to your domain only over HTTPS, for the number of seconds in max-age. With includeSubDomains, the rule covers every subdomain too.

The preload list goes further. Browsers ship with the list built in, so even the first visit to your domain uses HTTPS. The requirements at hstspreload.org are:

  • a valid certificate, and a redirect from HTTP to HTTPS on the same host
  • every subdomain served over HTTPS, including internal ones
  • max-age of at least 31536000 (one year), plus includeSubDomains and preload
  • the HSTS header on redirects served from the HTTPS site as well

Roll out in stages. The preload site suggests starting with max-age=300, then one week (604800), then one month (2592000), watching for breakage at each step. Removal from the preload list takes months to reach users, so be sure every subdomain can support HTTPS long-term.

X-Content-Type-Options

X-Content-Type-Options: nosniff stops browsers from guessing a file's type from its content. Pair it with a correct Content-Type on every response, including a charset for HTML, such as text/html; charset=UTF-8.

frame-ancestors and X-Frame-Options

Clickjacking works by loading your page invisibly inside a frame on another site. The CSP directive frame-ancestors controls who may frame you, and OWASP notes that it supersedes X-Frame-Options in browsers that support it.

Two details catch people out. According to MDN, frame-ancestors does not fall back to default-src, so you must set it explicitly. It also has no effect in a <meta> tag; it only works as a header. For older browsers, it does no harm to send both:

Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY

Use frame-ancestors 'self' if your own site frames its pages. Avoid X-Frame-Options: ALLOW-FROM, which is obsolete. MDN notes that modern browsers ignore the whole header when they see it.

Referrer-Policy

This header controls how much of the current URL is sent to other sites in the Referer header. If you set nothing, browsers default to strict-origin-when-cross-origin, which sends only the origin to other sites over HTTPS. OWASP still recommends setting it explicitly. Use no-referrer or same-origin if your URLs can contain tokens or personal data.

Permissions-Policy

Permissions-Policy turns off browser features your site does not use. An empty allowlist, (), disables a feature for your page and for every frame inside it:

Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=()

If a feature is needed, allow it narrowly, for example camera=(self).

COOP and CORP

Cross-Origin-Opener-Policy: same-origin puts your page in its own browsing context group. A page that opens yours with window.open() then cannot keep a reference to it, which closes off a class of cross-site leak attacks (XS-Leaks). If you rely on popups for single sign-on or payments, same-origin-allow-popups keeps those working.

Cross-Origin-Resource-Policy: same-site stops other sites from embedding your resources, such as images or scripts. Cross-Origin-Embedder-Policy: require-corp is only needed for cross-origin isolation, which features like SharedArrayBuffer require. Leave COEP out unless you need those features, because it blocks cross-origin resources that do not opt in.

Headers to retire

HeaderStatusWhat to do
X-XSS-ProtectionDeprecated and non-standard; MDN warns it can create XSS flaws in otherwise safe sitesRemove it, or send 0, and rely on CSP
Expect-CTDeprecatedRemove it
Public-Key-PinsRemoved from Chromium in 2018Remove it; use Certificate Transparency and CAA records instead
X-Frame-Options: ALLOW-FROMObsoleteUse frame-ancestors
Server, X-Powered-ByReveal software and versionsRemove them or use a neutral value

Where to start

  1. Check what your site sends today with our security headers checker.
  2. Add the low-risk headers first: X-Content-Type-Options, Referrer-Policy, Permissions-Policy and frame-ancestors.
  3. Deploy HSTS with a short max-age and raise it in stages. Consider preload only when every subdomain is ready.
  4. Run CSP in report-only mode for a few weeks, fix violations, then enforce it.

If you are building a new application and want these designed in from the start, our secure software development team can help.

How we can help

Security headers scanner

Grade a website’s HTTP security headers, cookies and TLS certificate, with a fix for every finding.

Contact us
AngelForce team

Written by the AngelForce team: security consultants with CEH, LPT Master and CISA certifications who test, monitor and defend systems for organisations across Europe and Türkiye.

Written with AI assistance from public sources listed below, and reviewed by the AngelForce team before publication.

Sources

  1. cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
  2. cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html
  3. hstspreload.org/
  4. developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API
  5. developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-XSS-Protection
  6. developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy

Articles about Web security

Tell us what you need to protect

Describe your environment and what you want to achieve. We will reply by email.

Contact us
info@angelforce.eu

AngelForce OÜ · Tallinn, Estonia