CentralCSP
Security headers

Deprecated headers

HPKP and Expect-CT are dead TLS experiments that browsers removed. What they were, why they died, and why the fix is to delete them.

Last update:

Two TLS security headers came and went: Public-Key-Pins (HPKP) and Expect-CT. Both tried to protect sites against misissued certificates, HPKP by pinning public keys in the browser and Expect-CT by requiring Certificate Transparency (CT) before it was mandatory.

Browsers have removed both. If a scan finds either header on your site, the fix is to delete it.

Removed from browsers

Every browser ignores both headers today: HPKP and Expect-CT were both removed from browsers years ago. HPKP was removed because a lost or hostile pin could lock users out of a site (self-DoS and hostile pinning); Expect-CT became pointless once Certificate Transparency was enforced by default. Details in Browser support.

There is no replacement header to send. What superseded them, Certificate Transparency and CAA DNS records, works without any response header, so the only action these headers call for is removing them from your server configuration.

At a glance

HeaderStatusWhat it wasWhat to do
Public-Key-Pins⚠️ DeprecatedPinned CA public keys in the browserRemove
Public-Key-Pins-Report-Only⚠️ DeprecatedReport-only pinningRemove
Expect-CT⚠️ DeprecatedOpt-in Certificate Transparency enforcementRemove (CT is the default)

Public-Key-Pins (HPKP)

HTTP Public Key Pinning (RFC 7469, 2015) let a site pin the public keys its certificate chain had to contain. The header carried one or more pin-sha256 values, each a base64 hash of a public key (its SPKI), plus a required max-age, with directives separated by semicolons. The browser cached the pins and refused any future connection to the host whose certificate chain did not include one of the pinned keys. The spec required a backup pin, a pin for a key not in the current chain, as a recovery path, and a Public-Key-Pins-Report-Only variant reported instead of blocking.

This is what a deployment looked like. Never send it today; no browser reads it.

Historical example, do not send this today
Public-Key-Pins: pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=";
  pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=";
  max-age=5184000; includeSubDomains

Three problems killed it:

  • Self-DoS. Lose the pinned keys and every returning browser refuses your site until max-age expires, and 60-day values were typical, so a single mistake could lock every returning visitor out of the site for weeks.
  • Hostile pinning, known as RansomPKP. An attacker who briefly controls the server can pin keys only they hold and ransom them back; browsers keep rejecting the legitimate certificate after the site owner recovers. Chrome's removal rationale names exactly these "risks of denial of service and hostile pinning".
  • Near-zero adoption. Usage peaked around 3,500 sites in the top 1 million and had fallen to about 650 by September 2019, many of them misconfigured.

Chrome removed HPKP years ago, and Firefox, which had shipped it earlier, removed it soon after, since which the headers are silently ignored. Safari and Edge never shipped it.

Expect-CT

Expect-CT (RFC 9163) was an opt-in bridge to Certificate Transparency enforcement, for the period before CT became mandatory. It took a required max-age, an enforce flag, and a report-uri, separated by commas rather than HPKP's semicolons (a classic source of confusion between the two). Both headers reported through bespoke report-uri JSON POSTs that predate the Reporting API; any endpoints still configured for them are dead letterboxes today.

Historical example, do not send this today
Expect-CT: max-age=86400, enforce

The header became pointless by policy. Chrome requires CT for all publicly trusted certificates issued after April 30, 2018, so a site had nothing left to opt into; the last pre-CT certificates expired around June 2021. The RFC was only published in 2022, as Experimental, the same year Chrome removed the header.

Chrome removed Expect-CT years ago. Firefox and Safari never implemented it.

What replaced them

  • Certificate Transparency, on by default. Chrome requires CT for certificates issued after April 30, 2018, and Apple platforms for certificates issued after October 15, 2018. Certificate authorities embed the SCT proofs in the certificate itself; sites send nothing.
  • CAA DNS records (RFC 8659) declare which certificate authorities may issue for your domain.
  • In-app pinning for clients you control (mobile apps, machine-to-machine clients), per the OWASP Pinning cheat sheet.

One honest caveat: CT and CAA together do not cover quite every feature HPKP had, but they give comparable protection against misissuance without HPKP's risks.

Risks of keeping them

There is no enforcement risk, because nothing parses these headers anymore; a stale pin cannot lock anyone out via the header today. The costs are operational: dead bytes on every response, scanners and pentest reports that flag them as deprecated findings, and the signal a 2017-era dead header sends about an unmaintained security configuration. Run your site through the security headers scanner to check whether either header is still in your responses.

Recommendation

Remove Public-Key-Pins, Public-Key-Pins-Report-Only, and Expect-CT from your server configuration. Certificate Transparency is enforced by default for every certificate a public CA can issue today, per the Chrome CT policy, so there is nothing to configure in its place. If you genuinely need pinning for a client you control, pin in the client following the OWASP Pinning cheat sheet, never through a browser header.

Browser support

Public-Key-Pins:

BrowserSupport
ChromeSupported for a while, removed years ago
FirefoxSupported for a while, removed years ago
OperaSupported for a while, removed years ago
SafariNever shipped
EdgeNever shipped

Expect-CT:

BrowserSupport
Chrome and Chromium browsersSupported for a while, removed years ago
FirefoxNever implemented
SafariNever implemented

FAQ

Is Expect-CT still needed?

No. Remove it. Chrome dropped support for the header, and it was Chromium-only to begin with. Certificate Transparency is now enforced by default for every certificate a public CA can issue, so there is nothing left for the header to opt into. It is dead bytes that scanners flag as a deprecated finding.

What replaced HPKP?

Header-side, nothing to send. Certificate Transparency runs by default, so CAs embed the proof in the certificate and your site configures nothing. CAA DNS records let you declare which CAs may issue for your domain. For clients you control, such as mobile apps, pin in the client using the OWASP pinning guidance, never through a browser header.

Should I remove Public-Key-Pins?

Yes, remove both Public-Key-Pins and its report-only form. No current browser processes them; every engine that shipped HPKP removed it. Sending it today enforces nothing, but it adds dead bytes to every response and signals an unmaintained security config that scanners and pentest reports flag.

See also

Sources

On this page