SSL pinning in React Native: 4 approaches, 3 of them broken
SSL pinning protects against TLS interception. Done wrong, it bricks your app the day your TLS certificate rotates. The wrong-vs-right is mostly about pinning the public key (not the certificate) and embedding backup pins.
Most consumer apps don't need pinning. The operational risk usually outweighs the threat-model benefit. Banking, healthcare, and regulated payments are the cases where it's worth it.
If you do pin, use native config (App Transport Security on iOS, Network Security Config on Android). Pin the public key. Always carry a backup pin. Plan rotation.
SSL pinning sounds like a security upgrade. It's also a famous foot-gun. A cert rotates, your app stops working for everyone simultaneously, and you find out the urgent app update needs App Store review.
This post is the four common approaches in React Native, why three of them are usually broken in some way, and the operational pattern that makes pinning a real security control instead of a self-imposed outage.
What pinning is and isn't
Pinning narrows TLS trust. By default, your app trusts any certificate that chains up to a root CA in the device's trust store. There are hundreds of CAs. An attacker who can add a root cert to the trust store — through a corporate MDM, a jailbreak tool, or instrumentation like Frida — can issue themselves a certificate for your domain and intercept traffic.
Pinning replaces "trust any CA-chained cert for this domain" with "trust only these specific certs (or these public keys)." The interception fails because the attacker's cert doesn't match.
What pinning doesn't protect against:
- An attacker who compromises your server directly
- An attacker who steals the actual private key from your server
- A malicious or compromised CA issuing a cert that matches your real public key (rare with key pinning, possible with cert pinning)
- Reverse engineering of the app to find the pinned values and bypass them
Pinning is one defense among many. It's not magic.
When to actually pin
The threat model question: does your app face attackers who can install custom root certificates on victim devices?
Cases where the answer is genuinely yes:
- Banking apps, especially in markets with high mobile fraud rates
- Healthcare apps handling PHI under HIPAA or equivalent
- Regulated payments where pinning is named in the compliance standard (PCI-DSS for some configurations)
- Enterprise apps deployed into environments with corporate MITM proxies as a real threat
- Apps where the audit explicitly requires pinning
Cases where the answer is usually no:
- Most consumer apps. The threat model is theoretical and the operational risk is real.
- Internal tools. If your users are your employees, you control the device.
- Apps still in early product-market fit. Premature pinning is premature optimization with extra teeth.
Certificate pinning vs public key pinning
Two flavors. Different operational properties.
| Type | What you pin | Rotation behavior |
|---|---|---|
| Certificate pinning | The full X.509 certificate | App breaks the day the cert is replaced. Must ship app update before rotation. |
| Public key pinning | The hash of the public key (SubjectPublicKeyInfo) | App keeps working as long as the new cert uses the same key. Reuse the key across rotations. |
Public key pinning is what production deployments actually use. Certificate pinning is what tutorials usually show. The difference matters enormously the first time you rotate certificates without an app update lined up.
Approach 1: Native config (recommended)
The platform-native pinning options are the most robust. They're verified by the OS's own TLS stack — not by your code, not by a third-party library that might have its own bugs.
iOS — App Transport Security with pinning:
In your Info.plist, configure NSAppTransportSecurity with NSPinnedDomains. The OS handles verification at the network layer; your code calls fetch as normal.
Android — Network Security Config:
In res/xml/network_security_config.xml, configure <pin-set> entries with public key hashes. Reference the config from AndroidManifest.xml. The OS handles verification.
What this approach gets right:
- OS-level verification — no app code path to bypass
- Works with any HTTP client that uses the system networking stack (including React Native's
fetch) - Supports backup pins natively in both platforms
- No additional dependencies, no native module to maintain
Drawbacks: configuration is platform-specific (you maintain two configs), and Android Network Security Config has some quirks around debug builds and proxy use.
Approach 2: TrustKit (iOS)
TrustKit is an Objective-C library that adds richer pinning features on top of iOS's networking — backup pins, violation reporting, fine-grained control over which domains are pinned, and graceful fallback during pin failures.
Where TrustKit helps over native config:
- Reporting — TrustKit can phone home when a pin fails, giving you observability into rotation issues before users start complaining
- More granular control over pin policy per domain
- Better diagnostic logs when something goes wrong
Where it doesn't help:
- It's iOS-only. You still need Network Security Config for Android.
- It's a native dependency you maintain. The library is mature, but every native dep has a cost.
Worth it for security-sensitive apps that want observability. Overkill for most apps.
Approach 3: react-native-ssl-pinning
A community-maintained React Native library that ships its own pinning-aware fetch. Works on both platforms with a single API.
The structural problem: it provides its own fetch implementation. Your app's other HTTP code paths — Axios, image loading, WebView traffic, library-internal network calls — don't go through it. They use the standard networking stack and aren't pinned.
That's usually not what you want. The whole point of pinning is to protect against interception. If half your network traffic isn't pinned, the protection is partial.
The library also has the usual third-party-React-Native-library risks: maintenance state varies, peer ranges may not match current React Native, New Architecture support has been inconsistent.
Use this approach only if:
- All your network traffic goes through a single client (often
fetchwrapped in a thin abstraction) - You can't reasonably configure native pinning
- You've audited the current maintenance state of the library
Approach 4: Custom native module
You write your own native pinning implementation. URLSession delegate on iOS, OkHttp CertificatePinner on Android. You expose it through a React Native module.
This is the heaviest option. Worth it when:
- You have specific requirements that no other approach covers
- You're already maintaining substantial native code and adding one more module is incremental
- Compliance requires a specific implementation pattern that off-the-shelf libraries don't provide
For everyone else, this is over-engineering. The native config approach is usually better.
Pin rotation strategy
The hardest part of pinning isn't shipping the initial pins. It's rotating without taking the app down.
The pattern that works:
- Pin the public key, not the certificate. If you reuse the key across rotations, most rotations don't require an app update.
- Always ship at least one backup pin. Two pins in the app. The active pin matches the current cert. The backup pin matches a future cert (or key) you've generated but haven't deployed.
- Plan rotation in two steps. First, ship an app update adding a new backup pin. Wait for adoption — at least 90% of installs on the new version. Then rotate the server cert to one matching the backup pin (now active). Then ship another app update adding the next backup pin and dropping the old one.
- Monitor pin failures. Whether you use TrustKit's reporting or roll your own, know when pin checks fail. Failures should be near-zero. Spikes mean either an attack or a rotation problem.
Failure modes
The ones we see most:
- No backup pin. Cert rotates, every install breaks simultaneously. App Store update takes hours. Worst-case scenario realized.
- Backup pin but no rotation plan. Team forgets which pin is active vs backup. Eventually both expire. Same outcome as no backup pin.
- Pinning the wrong layer. Pinning the leaf certificate when you should pin the intermediate CA, or vice versa. Works until a specific kind of rotation, then breaks.
- Pinning a CDN-served domain. Some CDNs rotate certs on a schedule the app can't predict. Pinning becomes a constant battle.
- Library wraps fetch but doesn't wrap image loading. Mixed pinning state. The protection model assumes uniform pinning.
- Debug builds bypass pinning via environment toggle, and the toggle ships to production. Has happened more than once.
Every one of these is preventable with a careful operational plan. The actual TLS code is the easy part.
Doing a security audit on a React Native app?
Pinning is one item on a longer list. Our security audit covers the full surface — dependency CVEs, secure storage misconfiguration, network configuration, and the React Native specific items that don't appear in generic mobile audits.
Frequently Asked Questions
What is SSL pinning in mobile apps?
SSL pinning (more accurately TLS pinning, since SSL itself is deprecated) is the practice of restricting which TLS certificates or public keys your app will trust when connecting to a specific server. Instead of trusting any certificate that chains to a root CA in the device trust store, you trust only the specific certificate or public key you embedded in the app. It blocks an attacker who can install a custom root CA on the device — common in corporate MITM proxies, jailbreak tools, and instrumentation frameworks.
Does my React Native app need SSL pinning?
Probably not. Most consumer apps don't need it — the threat model (network attackers with custom root certs) doesn't justify the operational risk (bricking the app when certs rotate). The apps that should pin are banking, healthcare, regulated payments, and apps with explicit compliance requirements that name pinning. If your security audit doesn't explicitly require pinning, the cost-benefit usually doesn't favor it.
What's the difference between certificate pinning and public key pinning?
Certificate pinning embeds the entire X.509 certificate. When the cert rotates, the app stops working until you ship an update. Public key pinning embeds only the public key hash, which can stay constant across multiple certificate rotations as long as the same key is reused. Public key pinning is more operationally forgiving, which is why production deployments almost always use it. Cert pinning is more common in tutorials but worse in practice.
How do I implement SSL pinning in React Native?
Four approaches. Native config (iOS App Transport Security pinning, Android Network Security Config) is the most robust and least React-Native-specific — Apple and Google handle the verification natively. TrustKit is an iOS-specific library that adds backup-pin and reporting features. react-native-ssl-pinning is a community library that ships with its own fetch implementation. Custom native modules are the heavyweight option for teams with specific requirements. Most teams that need pinning should start with native config.
What happens when an SSL-pinned app's certificate rotates?
Without backup pins, every installed copy of the app stops working immediately. Users see opaque connection errors. Support gets overwhelmed. You ship an emergency app update — but App Store review takes hours to days, during which the app is broken for everyone. With backup pins, the second pin matches the new certificate (or its key), and the app keeps working. The backup-pin strategy is the difference between SSL pinning being a real security control and being a self-imposed denial-of-service.
What is a backup pin?
A backup pin is a second (or third) certificate or public-key hash you embed in the app alongside the primary pin. The TLS handshake succeeds if the server's cert matches any of the embedded pins. You rotate the server's cert to one matching a backup pin, then ship an app update adding a new backup pin and removing the now-active one. Without this strategy, certificate rotation is high-risk; with it, rotation is routine.