Certificate pinning in a mobile app means hardcoding which specific certificate, or which specific public key, your app will trust for its own backend API — rejecting a connection even if the presented certificate is otherwise validly signed by a trusted CA, unless it matches the pinned value. Our general introduction to certificate pinning covers the underlying concept; this guide focuses exactly on implementing it in a mobile app, where the trade-offs play out differently than in a browser.
Why mobile apps specifically are a common place to see this
A mobile app, unlike a browser, talks to exactly one backend (or a small, known set of them) for its entire lifetime — making the normally-costly trade-off of certificate pinning (losing the flexibility to trust any publicly valid CA) much less of a downside, since the app was never going to need to trust arbitrary domains in the first place. This makes mobile apps, along with dedicated IoT devices, one of the more common legitimate use cases for pinning.
Pinning the public key rather than the certificate itself
Pinning the certificate's exact bytes means the pin breaks the moment that certificate is renewed — even a routine, uncompromised renewal. Pinning the public key instead survives a normal certificate renewal as long as the same key pair continues to be used, which is why most real implementations pin the public key (or a hash of it) rather than the full certificate.
iOS implementation, in outline
// Using URLSession's server trust evaluation
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
// Extract the server's public key, compare its hash
// against your pinned value, then call the completion
// handler with .useCredential or .cancelAuthenticationChallenge
}
Android implementation, in outline
// Using OkHttp's CertificatePinner
val client = OkHttpClient.Builder()
.certificatePinner(
CertificatePinner.Builder()
.add("api.yourapp.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build()
)
.build()
The real risk: locking out your own users
The single most common failure mode with mobile certificate pinning isn't a security breach — it's an operational one. If you rotate your backend's certificate (or its key) and forget that older, already-installed versions of your app have the old pin hardcoded, every user on an older app version loses connectivity to your API entirely, with no way to fix it except pushing an app update and waiting for users to in practice install it — which, unlike a website, isn't instant.
Pinning to an intermediate or your own root, not the leaf
Pinning to your CA's intermediate certificate, rather than your own frequently-renewed leaf certificate, is the more common practical approach — it survives routine leaf renewals as long as you stay with the same CA, while still substantially narrowing trust compared to accepting any publicly trusted CA at all. Some organizations go further and pin to their own private root, used exclusively for the mobile API backend, giving full control over the rotation schedule at the cost of running that infrastructure themselves.
Always keeping a backup pin
Every serious pinning implementation includes at least one backup pin — a second, currently-unused key already pre-configured in the app — specifically so a planned certificate or key rotation can happen by first deploying the new pin as the backup, waiting for the update to propagate to the installed base, then promoting it to primary. Pinning to only a single value with no backup removes your ability to rotate at all without a coordinated, risky simultaneous change on both client and server.