TrustPin™
TrustPin™
Learn · Runbook

How to Rotate Certificates
in a Pinned Mobile App

Most pinning incidents are self-inflicted: a certificate renews with a new key and the app's pins do not follow. This runbook covers the order of operations that avoids the lock-out, whether you update pins remotely or still ship them in the binary.

By the TrustPin engineering team · Published 10 October 2026 · About 7 minutes

The short version

Pin the next key before the server uses it, switch the server, keep the old pin until the long tail of devices has the new set, and automate the whole loop so it runs at every renewal. If your pins live in the binary, the same order applies but every step costs a release, which is why the schedule of shrinking certificate lifetimes pushes teams toward remotely updatable pins.

Why rotation breaks pinned apps

A pin identifies either a certificate or, more usefully, its public key. A certificate hash changes at every renewal. An SPKI hash changes whenever the key pair changes, and in practice that is also at every renewal: AWS Certificate Manager always generates a new key pair, certbot does unless you pass --reuse-key, and Cloudflare's managed certificates rotate keys. So every renewal is, for the app, a new identity.

Renewals are also getting more frequent. Under the CA/Browser Forum's schedule the maximum lifetime of a public TLS certificate fell to 200 days in March 2026 and falls to 100 days in March 2027 and 47 days in March 2029. Put that next to a store review of a day or two and a user base where a long tail never updates, and a pin set that lives in the binary cannot keep up. The explainer covers the background; this page is about the procedure.

Before you start: inventory and ownership

  • List every hostname the app pins, including CDN hostnames, regional endpoints and third-party APIs you chose to pin.
  • For each host, record who issues and renews the certificate, whether the key is reused at renewal, and when the next renewal is due.
  • Confirm what the app does when a pin fails: fail closed (requests stop), or fail open (pinning silently stops). Fail closed is the point of pinning; make sure you know which you have.
  • Find out how long it takes your installed base to pick up a change: for a pin set in the binary, that is your release adoption curve; for a remote pin set, it is the fetch interval plus how often the app is opened.

The pin lookup tool shows what a hostname is currently serving, and what has already been issued for it in Certificate Transparency. Run it for every host on the list.

Step 1: pin the next key before the switch

The rule that prevents almost every lock-out: the app must trust the new key before the server presents it. That means a period where both the current pin and the next pin are valid. Publicly trusted certificates are logged to Certificate Transparency at issuance, so a certificate issued ahead of deployment is observable before the switch. Where your tooling installs a certificate the moment it is issued, as ACME clients do by default, issue first and deploy later. Pin the next key as a backup, deploy the certificate, then retire the old pin once it expires.

With TrustPin this is one command. It reads the live certificate and every unexpired issuance in Certificate Transparency for the domain and stages all of their SPKI pins, so the overlap is maintained for you:

Stage pins for a domain, including certificates issued but not yet servedbash
trustpin-cli projects refresh-certs <org> <project> --domain api.example.com --remove-expired

If the host is not reachable from the internet, stage the pins explicitly from the certificate instead; the CLI command reference covers both paths.

Step 2: rehearse in staging, then watch production

A first rotation under pinning, or a new host added to the pin set, should not meet production traffic untested. Use one TrustPin project per app and environment, which is the documented practice: publish the new pin set to the staging project first, run a staging build against the renewed certificate, and only then publish to the production project. The release build stays in strict mode throughout. Permissive mode is set in the app's configuration file, not in the published pin set; it lets unregistered hosts through, it exists for local development, and it belongs in a debug-only file, never in a release build.

Monitoring comes from the SDK's validation listener. It is observe-only, so it cannot weaken enforcement, and it receives every definitive verdict; on iOS, Android and Flutter a mismatch also carries the certificate that was presented. Wire it into your telemetry before the rotation so a mismatch shows up on the first failed connection rather than in a support ticket. A publish propagates within minutes, and a device refreshes a configuration older than ten minutes on its next use, so a correction reaches active devices quickly.

Step 3: plan for devices that are offline

Some devices will not fetch the new configuration before the server switches: a phone in a drawer, an app not opened for a month. Two things protect them. First, the SDK's order of operations: on the next launch it refreshes a configuration older than ten minutes before the first pinned request, so a device that comes back online learns the new pin before it talks to your API. Second, the lead time from step 1: publish the new pin 7 to 14 days before the switch, as the docs recommend, so every device opened in that window already holds both pins by the time the server changes. A device that is long idle and also cannot reach the CDN on its first launch back validates against the last configuration it verified until the CDN is reachable again; the lead time is what keeps that group small. The one case neither covers is a first launch with no network at all, where there is no verified configuration to fall back on. TrustPin's embedded configuration covers that case, but only for apps protected by runtime application self-protection (RASP), and it has to be regenerated at every release; an unprotected app must not ship one.

Step 4: know what rollback means

With signed, versioned configurations there is no "roll back" in the sense of re-publishing an old file: devices reject configurations older than the one they hold, which is what stops an attacker replaying a captured pin set. Rolling back therefore means publishing a new configuration that restores the previous pins. Keep the previous key's pin in the set until its certificate expires and you always have that option. If a rotation goes wrong on the server side, revert the server to the previous certificate; the devices still hold its pin.

Step 5: automate it

At 47-day lifetimes a rotation is no longer an event; it is a schedule. The loop belongs in a pipeline that runs on a timer and, where the issuer emits one, on the renewal event. The shape that works:

  1. Install a pinned CLI version and authenticate with a token from the pipeline's secret store.
  2. Rehearse the signature with --dry-run so bad credentials fail before anything is staged.
  3. Stage with projects refresh-certs for every domain in the project.
  4. Compare the project's configuration version before and after. If nothing changed, stop: publishing an unchanged configuration is rejected.
  5. Sign and publish, behind an approval step for production if you want a human in the loop.
  6. Verify what is live with projects jws --verify, and alert on failure.

The CLI DevOps guide has the GitHub Actions workflow and the AWS flow, and the DevSecOps page shows the same loop for GitHub Actions, GitLab CI, Jenkins and Azure DevOps side by side. If your certificates live in AWS Certificate Manager, read certificate pinning with AWS Certificate Manager for the renewal event and the per-region detail.

If you still pin statically

The same order applies, and each step is a release. Compile the next key's pin in as a backup at least one release before the switch, and leave the old pin in for at least one release after. Time renewals to your release train rather than the other way round, which means reusing keys where your issuer allows it and requesting certificates ahead of deployment where it does not. Set the Android Network Security Config expiration deliberately, knowing that pinning turns off when it passes. And budget for the fact that a lock-out, when it comes, lasts as long as your slowest users take to update.

When that budget stops being acceptable, the change is architectural, not procedural: dynamic certificate pinning moves the pin set out of the binary while keeping the trust anchor in it.

Frequently asked questions

Can I rotate a certificate without updating the app?

Only if the app can receive a new pin set without a release. With static pinning the pins are compiled into the binary, so rotating to a new key means shipping a new version and waiting for users to install it; the only mitigation is to compile the next key's pin in as a backup before the switch. With dynamic pinning the new pin is published to installed apps as a signed configuration, verified on the device, and the server can switch once the devices have it.

How far ahead should the new pin be published?

Before the server starts presenting the new certificate, with enough margin for devices to fetch the configuration. TrustPin's docs recommend an overlap of 7 to 14 days for mobile apps, longer if your users go weeks without opening the app; server-to-server clients with guaranteed refresh cycles can work with hours. A certificate appears in Certificate Transparency at issuance, so when it is issued ahead of deployment its pin can be staged as soon as it exists.

Does the pin change when a certificate renews?

The SPKI pin changes whenever the key pair changes. AWS Certificate Manager generates a new key pair at every renewal, Let's Encrypt's certbot does unless you pass --reuse-key, and Cloudflare's managed certificates rotate keys. A certificate hash changes at every renewal regardless. Plan for a new pin at every renewal unless you have confirmed your issuer reuses the key.

Further reading

Make rotation a publish, not a release

Start free: one project, two domains, every SDK, and the CLI that stages the next pin before the server switches.