Certificate Pinning: Risks, Trade-Offs, and What I Recommend Instead

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:

  1. ship clients that trust both old and new pins
  2. deploy the new certificate/key on the server
  3. confirm clients accept it
  4. 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.

Scroll to Top