Legacy security headers you can retire
CentralCSP Team ·
Last update:
Security scanners and rating agencies still flag old response headers, and teams still copy them from blog posts written a decade ago. The result is header sets that carry obsolete directives, sometimes ones that actively do nothing or that a newer policy already covers. This is a short audit: which legacy headers to retire, what replaces each, and the few old ones that are genuinely still worth sending. Full reference: legacy security headers.
| Legacy header | Verdict | Replacement |
|---|---|---|
X-Frame-Options | ⚠️ Keep as fallback | CSP frame-ancestors |
X-XSS-Protection | ❌ Retire (send 0) | A CSP without 'unsafe-inline' |
Feature-Policy | ❌ Retire | Permissions-Policy |
CSP report-uri | ⚠️ Keep as fallback | CSP report-to + Reporting-Endpoints |
Expect-CT | ❌ Retire | Nothing, Certificate Transparency is now enforced by default |
Public-Key-Pins | ❌ Retire | Nothing, HPKP was removed from browsers |
X-Content-Type-Options | ✅ Keep | None, still current |
Strict-Transport-Security | ✅ Keep | None, still current |
The rest of this post takes each row in turn.
X-Frame-Options, replace with frame-ancestors
X-Frame-Options was the original clickjacking control: it said who could put your
page in a frame. It has aged badly. The ALLOW-FROM value is obsolete, and modern
browsers ignore the entire header when they encounter it, so a policy built on
ALLOW-FROM protects nobody. The CSP
frame-ancestors
directive is the modern replacement, it supports multiple origins, wildcards,
and 'none'.
Content-Security-Policy: frame-ancestors 'none'When both are present, a supporting browser uses frame-ancestors and ignores
X-Frame-Options, so you can keep X-Frame-Options: DENY or SAMEORIGIN as a
fallback for very old browsers. But frame-ancestors is the one doing the work
today, and X-Frame-Options vs frame-ancestors
walks the two through side by side. If you cannot set CSP response headers at all, see clickjacking protection when you cannot set a CSP header.
X-XSS-Protection, drop it
X-XSS-Protection switched on the browser's built-in XSS auditor. That auditor
turned out to be worse than nothing: it had bypasses, and attackers could abuse it to
selectively disable legitimate scripts, so Chromium removed it entirely. The header
now controls a feature that no longer exists in modern browsers. The common advice is
to explicitly send 0 to make sure no residual legacy behavior kicks in:
X-XSS-Protection: 0Real XSS protection comes from a CSP that blocks inline scripts: see removing unsafe-inline and using nonces, hashes, and Trusted Types.
Feature-Policy, replace with Permissions-Policy
Feature-Policy controlled access to browser features (camera, geolocation, and so
on) but is deprecated. Permissions-Policy is its standardized successor, with a
revised allowlist syntax and the ability to report violations through the Reporting
API (Permissions-Policy explained). Migrate the directives across; the feature names are largely the same, the
syntax is not.
Permissions-Policy: geolocation=(), camera=(self)report-uri, move to report-to
The CSP report-uri directive still works but is deprecated in favor of the
report-to directive plus the Reporting-Endpoints header. Because browsers that
support report-to ignore report-uri, you can send both during the transition
without double-reporting. The full migration is in
report-uri vs report-to.
Expect-CT and Public-Key-Pins, delete them
Two headers are not deprecated so much as gone. Public-Key-Pins (HPKP) let a site pin
the certificates browsers would accept for it, and a single mistake could lock a domain
out of every browser for the pin's lifetime. It was removed from browsers rather than
fixed. Expect-CT asked for Certificate Transparency enforcement, which browsers now
apply by default, so the header has nothing left to ask for.
Neither has a replacement, because neither needs one. Delete them; anything still sending them is shipping bytes no browser reads. Both are covered in the deprecated headers reference.
Headers that are not legacy, keep these
A cleanup is also where people accidentally remove headers they should keep. Two
older headers are not obsolete and have no reporting-based replacement:
X-Content-Type-Options: nosniff, which stops MIME-type sniffing, and
Strict-Transport-Security (HSTS), which enforces HTTPS. Both are still recommended.
Do not drop them while retiring the others.
Find what your site actually sends
Before you change anything, see the current state. A scan tells you which legacy headers a live site still sends and which modern replacements are missing, so you retire and replace in one informed pass rather than guessing. CentralCSP's free security headers scanner gives you that before-and-after list against the table above, and the guide on how to improve your security headers grade takes it from there.
The replacements are also the reason retiring is safe to verify rather than assume:
frame-ancestors, Permissions-Policy and report-to all report violations through the
Reporting API, which the old headers never did.
Point them at a CentralCSP endpoint before you delete the legacy header and you can see
the modern control working on live traffic first.
Next steps
- Replace clickjacking protection: frame-ancestors.
- Replace Feature-Policy: Permissions-Policy.
- Build the CSP that replaces X-XSS-Protection: how to build a strong CSP.