The short answer
Certificate pinning tells a client to accept one specific certificate or public key for a host instead of trusting any certificate a public CA signs. It closes off certificate substitution, and it does so by turning every certificate rotation into a release event. What you pin decides how often that hurts: pin the leaf certificate and the pin dies at every renewal; pin the Subject Public Key Info and reuse the key at reissue, and the same pin survives. That distinction stopped being academic on March 15, 2026, when the maximum public TLS certificate lifetime fell to 200 days on the way to 47 days in March 2029. Browsers dropped pinning entirely years ago; it now lives almost exclusively in mobile apps and machine-to-machine links.
On this page
What is certificate pinning?
Certificate pinning is a client-side rule that narrows trust from "any certificate signed by a CA in my trust store" down to "this certificate, or this public key, for this hostname". The client still performs the normal chain and hostname checks, then applies one extra test: does something in the chain the server presented match a value baked into the client? If nothing matches, the handshake is abandoned, regardless of how valid the certificate is.
The threat it addresses is narrow and real. A public trust store holds well over a hundred root certificates, and any certificate authority in it is technically capable of issuing for your domain. Add corporate TLS interception appliances and locally installed roots on managed devices, and the set of parties who can produce a certificate your client will accept is larger than most teams assume. A pin cuts that set down to one.
What the definition leaves out is the part that decides whether pinning works in practice. A pin is a piece of configuration that lives inside a deployed client, and certificates change on the server. Everything difficult about pinning follows from that one asymmetry.
What do you actually pin?
Almost every real implementation pins a SHA-256 hash of the Subject Public Key Info, not the certificate. SPKI is the DER structure inside an X.509 certificate that carries the public key together with its algorithm identifier. Hashing it rather than the whole certificate matters because the SPKI is stable across reissues that keep the same key pair, while the certificate hash changes if so much as the serial number moves.
Android's network security configuration accepts only SHA-256 digests of the SPKI. Apple's App Transport Security calls the same value SPKI-SHA256-BASE64. OkHttp's CertificatePinner takes pins in the form sha256/… and states outright that it pins the Subject Public Key Info. Three ecosystems, one design decision, made for the same reason.
You also choose a height in the chain. Pinning the leaf binds you to one server key. Pinning the intermediate binds you to one issuing CA and survives leaf renewals for years. Pinning the root barely constrains anything, since it accepts every certificate that root will ever sign. The structure of the certificate chain is worth being fluent in before you pick a height, because a pin at the wrong level is either useless or a scheduled outage.
Extracting the value is a two-command job with OpenSSL: pull the public key out of the certificate, then hash the DER form and base64 it. Our OpenSSL commands cheat sheet covers the inspection side, and the pin itself is a pipe away from the same output.
Why browser pinning is gone
Web pinning is dead and has been for years. HTTP Public Key Pinning, standardised as RFC 7469, let a site advertise pins in a Public-Key-Pins response header. Chrome removed support in Chrome 72. No current major browser honours the header. If you are looking for a way to pin your public website, there is not one.
The reason was not that pinning failed to work. It worked exactly as specified, which was the problem. A pin the browser had cached could not be withdrawn: lose the pinned key, misconfigure the header, or renew onto a key you had not pinned, and the site was unreachable for the full max-age, with no way to reach the affected users to tell them. Chromium's own removal notice named the two failure modes as self-inflicted denial of service and hostile pinning, against very low adoption.
Expect-CT filled part of the gap for a while, then went the same way: deprecated in Chrome 105 in August 2022 and removed in Chrome 107, because Certificate Transparency enforcement became unconditional and the header no longer bought anything. What remains in browsers is the preload pin list that Google and Mozilla maintain for a handful of their own domains, and you cannot get onto it.
Worth being precise about: "certificate pinning is deprecated" is true of the browser mechanism and false of the practice. Android, iOS, Java and Go clients all still support pinning, and OWASP's mobile testing guidance still treats it as a control worth verifying. The header died; the technique did not.
Why shorter lifetimes break pins
Shorter certificate lifetimes break leaf pinning by arithmetic, not by design. As of August 2026, CA/Browser Forum ballot SC-081v3 has already cut the maximum lifetime for public TLS certificates to 200 days, effective March 15, 2026. It falls to 100 days on March 15, 2027 and to 47 days on March 15, 2029. A deployment that pins leaf certificates goes from roughly one forced pin rotation a year to roughly eight.
Eight rotations a year is not a maintenance burden so much as a different release model. Each one requires a new client build, app store review where that applies, and enough time for the install base to actually update. The users who lag are the ones who break, and they break silently: from their side the app stops connecting, with no warning and nothing to click. Support tickets describe it as "the app stopped working", which is not a diagnosis anyone reaches quickly.
Teams running automated renewal on the 47-day schedule usually hit this from the other direction. Automation is the right answer to short lifetimes, and most ACME clients mint a fresh key pair on every renewal by default. Automation and leaf pinning, deployed together without thinking about keys, will take the pinned clients down on the first successful renewal.
The pin that survives renewal
An SPKI pin survives renewal whenever the new certificate carries the same key pair. Reissue from the original CSR, or generate a new CSR from the existing private key, and the Subject Public Key Info is byte-identical — so the SHA-256 digest your clients enforce does not move. The certificate is new; the pinned value is not. This is the single most useful fact about pinning and the one most often left out.
Whether you get that behaviour depends on how the certificate is obtained. With a commercial CA, reissue is a normal operation: you submit a CSR and the CA signs it, so keeping the key is a matter of submitting the same one. This is one of the quieter practical arguments for buying an SSL certificate with a reissue-on-demand workflow when pinned clients are in the picture — you decide when the key changes, rather than a renewal timer deciding for you.
With ACME, the default runs the other way. Most clients generate a fresh key for each order unless told otherwise; Certbot exposes --reuse-key precisely because operators need the other behaviour. Turn it on before you deploy pins, not after, and confirm the digest is stable across at least one real renewal cycle rather than assuming it.
There is a real trade-off here and it deserves stating plainly. A key you never rotate is a key whose exposure gets worse with time, and short certificate lifetimes exist partly to limit the damage window when a key leaks. Reusing a key indefinitely works against that. The workable compromise is a planned key rotation on your own schedule — every year or two, with backup pins already deployed — rather than an unplanned one every 47 days.
How pinning is configured on Android and iOS
Both platforms ship declarative pinning that needs no networking code. On Android you add a <pin-set> to the network security configuration; on iOS and macOS you add NSPinnedDomains under NSAppTransportSecurity in Info.plist. Both take base64 SHA-256 digests of the SPKI, and both let you list several pins so any one match is enough.
| Behaviour | Android network security config | Apple App Transport Security |
|---|---|---|
| Where it lives | res/xml config referenced from the manifest | NSPinnedDomains in Info.plist |
| What is hashed | SPKI, SHA-256 only | SPKI, SHA-256, base64 |
| Chain height | Any certificate in the chain may match | Separate keys for CA and leaf identities |
| Backup pins | Multiple <pin> entries; documented as something you should always include | Arrays accept several identities per domain |
| Expiry escape hatch | expiration attribute disables pinning on that date | No equivalent switch-off date |
Android's expiration attribute is the most interesting control on either platform, and Google documents both sides of it. Setting a date means an app that never gets updated stops enforcing pins instead of losing connectivity — which prevents the classic bricked-old-build failure. It also means an attacker who can wait until that date faces no pinning at all. Google says so explicitly in the same paragraph that recommends it. Pick the failure you would rather have, and write down which one you picked.
Where a networking library owns the connection, pinning usually belongs there instead. OkHttp's CertificatePinner takes sha256/ pins per hostname and accepts a connection if any pin in the set matches, which is the same any-of-many logic both platform mechanisms use.
Backup pins and the rotation runbook
A backup pin is the hash of a key pair you have generated and stored offline but never deployed. It is what turns a lost key or a forced CA change from an outage into a configuration update. Android's documentation states the requirement without hedging: without a backup key, being pushed onto new keys or a different CA means shipping an app update before connectivity comes back.
A workable set is three pins. The key currently in production. An offline replacement key that has never touched a server. And a pin covering your CA's issuing intermediate, which keeps you connected if both leaf keys somehow have to change at once. Store the offline key the way you would store any other root secret, because its whole value lies in not being reachable from the same systems as the live one.
The rotation sequence that does not break clients
- Ship a client release that pins both the current key and the replacement key. Nothing on the server changes yet.
- Wait for adoption. This is the step that takes weeks, and the step everyone tries to skip.
- Reissue the certificate onto the replacement key and deploy it. Clients on the new build match the second pin; clients on the old build are already broken if you skipped step two.
- Generate a fresh offline backup key and ship a release that pins the now-live key plus the new backup. You are back to a steady state.
Two operational notes that come out of that sequence. Pin rotation is gated by client adoption, so your slowest-updating users set the pace, not your certificate schedule. And emergency revocation does not fit inside it at all — a compromised key that has to be replaced today cannot wait for an app store review, which is exactly why the offline backup pin has to already be deployed before you need it.
When not to pin at all
Do not pin when you cannot ship a new pin faster than the certificate rotates. That single test rules out most candidates. It rules out public websites, where no browser would honour the pin anyway. It rules out clients calling third-party APIs, where the other side rotates on its own schedule and has no obligation to warn you. It rules out embedded devices with no reliable update channel, where a pin is a timer counting down to a field recall.
There are two further cases where pinning is a poor fit for reasons other than logistics. Corporate environments that intercept TLS with a locally installed root will see pinned apps fail, and the resolution is usually an exception list rather than a security win. And on the client side, pinning is not a meaningful defence against an attacker who controls the device: a rooted phone with a hooking framework attached bypasses application-level pins, which is why OWASP treats pinning as raising cost rather than as a boundary.
Where both ends are machines you operate, the calculus flips entirely. Trust is established out of band, updates are yours to schedule, and a public certificate may not be the right tool in the first place — a self-signed or private-CA certificate with a pinned key is a cleaner design than a public certificate you then constrain back down to one key. Mutual TLS covers the same ground with a proper credential lifecycle attached.
What to use instead of pinning
For anything served to browsers, the replacement for pinning is two controls that cost nothing to run and cannot lock anyone out. A CAA record restricts which certificate authorities are allowed to issue for your domain. Certificate Transparency monitoring tells you when a certificate for your domain is logged, whoever asked for it. Together they cover detection and prevention of misissuance, which was HPKP's actual purpose.
- CAA records — a DNS record naming your permitted CAs. Public CAs are required to check it before issuing, so it is a preventive control, and setting one up takes minutes.
- CT log monitoring — every publicly trusted certificate is logged, so watching Certificate Transparency logs surfaces an unexpected certificate for your domain within hours, with no client-side risk at all.
- DNSSEC and DANE — relevant for mail transport rather than the web, where SMTP clients can bind a TLS key through DNS instead of through an application pin.
- Expiry monitoring — pinning failures and expiry failures look identical from the outside, and short lifetimes make both more likely. Alerting on the certificate you are serving catches the more common of the two.
None of these stop a determined attacker who has already compromised a CA and can suppress logging. They do cover the realistic cases, and they fail open rather than taking your users offline, which over a five-year horizon is the trade most teams would take if anyone put it to them in those terms.
So should you pin?
Pin if you ship both endpoints, the connection is worth the overhead, and you can get a new pin onto clients within days. In practice that is a first-party mobile app talking to a first-party API, or two systems on a private link. Everything else is better served by CAA plus CT monitoring, and saying so is not a counsel of despair — it is where the browser vendors landed after running the experiment at web scale.
If you do pin, these five decisions carry the whole thing
- Pin the SPKI, never the certificate. The certificate hash changes at every reissue for no security benefit.
- Reuse the key at renewal, deliberately and with a rotation date on the calendar. A pin that survives renewal is the whole game.
- Deploy at least one offline backup pin before you need it. After you need it is too late by definition.
- Decide consciously about Android's
expirationattribute — a fail-open date, or indefinite enforcement and the recovery risk that comes with it. - Test the pin against a real renewal in staging before it reaches production, because the failure is total and arrives without a warning stage.
And treat the pin as part of the certificate's lifecycle rather than a one-off hardening task. Whoever owns certificate renewal needs to know a pin exists, because the day they quietly generate a fresh key is the day the app stops working for everyone who has not updated.
FAQ
Planning certificates around pinned clients?
Pinning survives renewal only when you control the key, which means reissue has to be a routine operation rather than a support ticket. Every certificate My-SSL sells includes free reissues, so you can submit the same CSR each time and keep the pinned SPKI unchanged for as long as you want it to hold.
Compare SSL certificates with free reissues