Certificate pinning has always sounded more appealing than it behaves in production.
The idea is attractive: instead of trusting any certificate that chains to a public CA in the system trust store, your client trusts one specific certificate or public key. That narrows the trust model and can reduce exposure to misissued certificates or a compromised CA. On a whiteboard, it looks clean.
In operations, pinning is one of those controls that can fail in a spectacularly self-inflicted way. I have seen teams plan pinning carefully, then hit certificate renewal, CDN migration, intermediate rotation, mobile app release lag, or emergency reissuance and suddenly realize they built themselves a lockout mechanism.
That does not mean pinning is always wrong. It means you should be very selective about where you use it and very deliberate about how you rotate it.
What certificate pinning is
Normally, a TLS client accepts a server certificate if:
- the hostname matches
- the certificate is time-valid
- the signature chain leads to a trusted root CA
- other local policy checks pass
With certificate pinning, the client adds another rule. It does not just trust “any valid CA-signed certificate for this host.” It trusts only a specific certificate, public key, SPKI fingerprint, or small allowed set of them.
That means a certificate could be perfectly valid in the public PKI and still be rejected because it does not match the pin.
There are several ways pinning shows up:
- pinning the exact leaf certificate
- pinning the leaf public key
- pinning an intermediate or dedicated issuing CA key
- pinning a certificate hash inside an app or client configuration
I strongly prefer public key pinning over exact leaf certificate pinning when pinning is unavoidable, because it gives you more room to renew the certificate without changing the underlying key immediately. Even then, you still need a rotation plan.
For general certificate structure background, this certificate chain explainer is helpful.
Why people wanted pinning in the first place
The original motivation is understandable.
The public web PKI trust model means your device trusts a large set of root CAs. If any one of them or one of their delegates misissues a certificate for your domain, an attacker with the right network position might be able to intercept traffic and present a “valid” but fraudulent certificate.
Pinning tries to reduce that risk by saying, effectively:
Even if the system trust store accepts this certificate, my application will only trust this expected key.
That is appealing for high-risk environments such as banking, sensitive APIs, or state-level threat models.
The problem is that pinning protects you by making trust more brittle.
HTTP Public Key Pinning (HPKP): why browsers killed it
If you have been around TLS for a while, you probably remember HPKP.
HPKP let a website tell browsers, via an HTTP header, which public keys should be trusted for future visits. It looked powerful because it allowed a site to defend itself against rogue issuance in the public CA ecosystem.
A simplified historical example looked like this:
Public-Key-Pins: pin-sha256="base64=="; max-age=5184000; includeSubDomains
Why HPKP was attractive
- reduced exposure to CA misissuance
- enforced by browsers after first contact
- could pin backup keys as well as active keys
Why HPKP was removed
The big issue was catastrophic failure risk.
If you pinned the wrong key, lost the pinned key material, forgot to maintain valid backup pins, or got attacked by someone who injected hostile pins, you could brick access to your own domain for users until the pin expired.
That is not a theoretical edge case. That is the central operational lesson of HPKP.
Browser vendors eventually decided the risk outweighed the benefit for the general web. HPKP is effectively a deprecated technology and should not be used for modern browser security policy.
If you still find old articles recommending HPKP headers for websites, treat them as historical documents, not current advice.
HSTS is not pinning
Teams sometimes mix up HSTS and pinning because both affect HTTPS trust behavior.
They are different.
HSTS
HTTP Strict Transport Security tells browsers:
- always use HTTPS for this host
- do not allow users to bypass certificate errors for the pinned HSTS period
It protects against downgrade and SSL stripping scenarios.
Pinning
Pinning says:
- only trust this specific cert or key material, not just any CA-valid one
HSTS is broadly recommended for public websites. Pinning is much narrower and more dangerous operationally.
I use HSTS routinely. I recommend pinning only in special cases.
Safer alternatives for most operators
This is the part I wish more teams started with.
CAA DNS records
CAA records let you specify which certificate authorities are allowed to issue certificates for your domain.
Example:
example.com. 3600 IN CAA 0 issue "letsencrypt.org"
example.com. 3600 IN CAA 0 iodef "mailto:security@example.com"
That does not provide the same client-side enforcement as pinning, but it reduces the chance of unwanted issuance if a CA follows policy correctly.
Check CAA records:
dig CAA example.com +short
CAA is not perfect, but it is much safer than HPKP ever was for normal website operators. There is a full guide on CAA records and issuance control.
Certificate Transparency (CT)
CT logs make certificate issuance publicly visible. That means you can monitor for unexpected certificates issued for your domain.
This is a much more practical safety layer for most organizations than hard pinning. It does not block a bad certificate by itself, but it makes detection dramatically better.
For most public websites, I would rather have:
- short-lived certificates
- CT monitoring
- CAA restrictions
- strong account security at the CA
than HPKP-style browser pinning.
If you want the details, this article on certificate transparency explains why it matters.
Short certificate lifetimes
Short-lived certificates reduce the time window of damage if a certificate is misissued or a key is exposed. This has become one of the more practical security improvements in the web PKI ecosystem.
Revocation and stapling
Revocation has limits, but it is still part of the story. OCSP stapling and short-lived certs together are usually more realistic than pinning for standard public sites. See certificate revocation with CRL and OCSP for the trade-offs.
Mobile app pinning: where pinning still genuinely makes sense
This is where pinning remains relevant.
For a controlled mobile app talking to a controlled backend, you can sometimes justify pinning, especially when:
- the app serves a high-value security function
- you need defense against hostile local trust stores or compromised network roots
- you control app release behavior reasonably well
- you can stage backup pins safely
Mobile banking apps are the classic example. Some enterprise security-sensitive apps also fit.
But even here, I treat pinning as a high-discipline control, not a default checkbox.
Android approaches
On Android, pinning is often implemented through network security configuration or inside the HTTP client stack.
A simplified network security config example:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-12-31">
<pin digest="SHA-256">BASE64SPKIHASHHERE=</pin>
<pin digest="SHA-256">BACKUPBASE64SPKIHASHHERE=</pin>
</pin-set>
</domain-config>
</network-security-config>
The backup pin is not optional theater. Without it, rotation becomes dangerous.
iOS approaches
On iOS, pinning is often done via URLSession delegate logic or through higher-level networking libraries. The same rules apply:
- pin SPKI if possible rather than the exact cert
- carry a backup pin
- plan for app update lag
The real mobile risk: release lag
This is the part developers underestimate.
If you rotate the server certificate or key and the new pin is not already trusted by the currently deployed app, users on older app versions break immediately. You do not control how fast the app store population updates.
That means safe mobile pinning usually requires:
- at least one backup pin baked into the app ahead of time
- overlap periods where both old and new acceptable keys exist
- an emergency fallback plan
- careful coordination between app and backend teams
I have seen certificate renewals treated as routine infrastructure work until a pinned mobile app turned them into a cross-team incident.
Pinning in API clients and internal services
Pinning can also appear in:
- CLI tools
- server-to-server API clients
- internal service meshes
- vendor integrations
This can be reasonable when the client deployment path is controlled and updateable. For example, a dedicated internal agent talking only to one API endpoint is a much safer place for pinning than a consumer website trying to use browser-level trust tricks.
Even then, I still ask whether a private CA, mTLS, or a narrower trust store would solve the problem with less operational fragility.
If you are designing high-assurance service authentication, this mTLS guide is often more useful than pinning alone.
How to rotate pinned certificates safely
If you decide pinning is justified, rotation planning matters more than the initial setup.
Prefer pinning the public key, not the leaf cert
Exact certificate pinning forces a change on every renewal. Pinning the SPKI or public key gives you room to renew the certificate while keeping the key stable for a controlled period.
That said, do not keep a key forever just to avoid rotation pain. That creates a different risk.
Always carry at least one backup pin
The backup should correspond to a different key you control and can deploy if the primary key becomes unusable.
Overlap old and new trust material
The safest rollout pattern is usually:
- ship clients that trust both old and new pins
- deploy the new certificate/key on the server
- confirm clients accept it
- later remove the old pin in another release
Document emergency paths
If the active certificate must be replaced during an incident, who updates the app? How fast? What happens to users who cannot update immediately? If that answer is “we will figure it out,” pinning is probably not mature enough for your environment.
Practical validation commands
Even if pinning logic lives in code, I still validate the server like a normal TLS endpoint first.
Inspect the live certificate and SANs:
echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -pubkey -fingerprint -sha256
Extract the SPKI hash for pinning workflows:
echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \
| openssl x509 -pubkey -noout \
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256 -binary \
| openssl enc -base64
That pipeline is one I keep around because it tells me exactly what hash a client pin would need to trust.
Common mistakes with pinning
Pinning the exact leaf certificate for routine web infrastructure
This creates unnecessary operational churn at renewal time.
No backup pin
That turns pinning into a single point of failure.
Forgetting the CDN or load balancer certificate path
If the public endpoint terminates TLS at a CDN, pinning the origin certificate may be irrelevant or actively wrong.
Treating HPKP as a current browser strategy
It is not. HPKP is obsolete and should stay in the past.
Mixing environments
I have seen staging and production share app builds while only one environment had the right pins. That is a predictable way to create confusing failures.
When pinning genuinely makes sense
I would consider it in scenarios like:
- a high-security mobile banking app
- a managed enterprise mobile app with controlled rollout
- a tightly managed API client distributed inside one organization
- a device fleet where the operator controls firmware updates and trust material
Even then, I would still challenge the design and make sure backup pins and rotation procedures are tested.
What pinning incidents usually look like
The ugly part of pinning incidents is that they often start as a routine maintenance task. A certificate is renewed, a CDN provider is changed, a mobile app release is delayed, or an emergency reissue happens after a key exposure. Then one segment of clients starts failing with what looks like a generic TLS error, while everything works fine in a browser or from a developer laptop that is not actually enforcing the pin.
That split view wastes time. Operations thinks TLS is healthy because the public certificate chain validates. Application teams think the backend changed unexpectedly. Security thinks a hostile certificate might be involved. In reality, the new certificate may be perfectly legitimate but simply not one of the pinned values in the deployed client population.
This is why I like to document pinning ownership very explicitly:
- who approves pin changes
- who owns backup pins
- which environments enforce them
- how clients recover if an emergency rotation happens outside a normal release cycle
If nobody can answer those questions quickly, pinning is probably providing more fragility than protection.
My practical recommendation for most operators
For public websites and ordinary APIs, I do not recommend certificate pinning as a default control.
I recommend this stack instead:
- HSTS for HTTPS-only enforcement
- CAA records to restrict who can issue
- CT monitoring to catch unexpected issuance
- short certificate lifetimes
- strong CA account security
- good key handling and rotation discipline
- revocation support where appropriate
That gives you meaningful protection without the same risk of locking yourself out of your own service.
If you are evaluating web PKI trust from a defensive angle, the revocation article fills in the rest of the picture.
Conclusion
Certificate pinning is powerful precisely because it narrows trust beyond the default CA model. That power is also why it breaks so hard when the operational plan is weak. HPKP taught the web this lesson the hard way and was removed for good reason.
For most operators, safer alternatives like CAA, certificate transparency monitoring, short-lived certificates, and strong TLS hygiene are the better answer. Pinning still has a place in high-assurance mobile apps and tightly controlled API clients, but only when you plan for backup pins, staged rotation, and the reality that certificate renewal is not an edge case. It is routine infrastructure work, and your trust model has to survive routine work.
One final rule I use: if the team cannot rehearse a certificate rotation in a calm maintenance window, it definitely should not rely on pinning during an actual incident. Controls that only work on perfect days are not really controls.
That may sound conservative, but pinning punishes optimism very quickly in production environments. I have yet to see shortcuts age well.