Setting Up SSL Pinning into React Native

Hello, my name is Luiz and in this tutorial i'll teach you how to add a new layer of protection on the mobile side that will defend your app from Man in the Middle Attacks. So without any further ado lets get started!
Why SSL Pinning is Needed?
Let’s say you’re connecting to your bank’s website or app. Generally, these apps rely on a system of Certificate Authorities (CAs)—big, trusted organizations that issue digital certificates. If your bank’s website or app presents a valid certificate issued by a recognized authority, your device says, “Yep, this checks out,” and sets up a secure (SSL/TLS) connection.
This works nicely most of the time. But there’s a weak spot: if somebody manages to create a fraudulent certificate that still looks valid to your device (for example, a certificate from a compromised or malicious CA), they can sneak in as a man-in-the-middle (MITM) using a proxy between your app and the server, then the attacker can then intercept your data, read it, or even alter it before sending to the server.
What is SSL Pinning
That’s where SSL Pinning (also called certificate pinning or public key pinning) steps in to save the day. Think of it as an extra piece of knowledge your app has—a secret handshake, if you will, that ensures it’s talking to the right server and nobody else.
Imagine you meet up with a close friend at a crowded venue. Sure, you could rely on them showing an official ID, but maybe you also have a special password or secret handshake you worked out in advance. If they don’t know that handshake, they’re not your friend—end of story.
Similarly, with SSL Pinning, your app “knows” exactly what the server’s certificate or public key looks like, because it’s stored—or “pinned”—in the app’s code. When your app tries to connect, it compares the server’s certificate to that pinned one. If they don’t match exactly, the app refuses to connect. This means even if a hacker has a seemingly legitimate certificate, it won’t fool the pinned check.
Setting up Pinning into Your React Native App
Securing your React Native app with SSL pinning can help protect against man-in-the-middle (MITM) attacks. Below, I’ll walk you through how to install and configure this library in both a plain React Native project (the “bare” workflow) and under the Expo Managed Workflow. We’ll also cover how to fetch the public key hashes from SSL Labs and how to test your pinning setup with Charles Proxy.
1. Installing
1.1 Plain React Native or Bare Workflow
- Install the library
# npm npm install react-native-ssl-public-key-pinning # or yarn yarn add react-native-ssl-public-key-pinning - Install pods on iOS
cd ios && pod install && cd .. - Rebuild your app
For iOS, open the.xcworkspacein Xcode and build. For Android, usenpx react-native run-androidor open it in Android Studio.
1.2 Expo Managed Workflow (Step-by-Step)
Because Expo Go does not allow custom native modules, you can’t test SSL pinning using the default Expo Go client. Instead, you’ll need to create a custom development build or use a production build. Here’s the process:
- Install the library
# npm npm install react-native-ssl-public-key-pinning # or yarn yarn add react-native-ssl-public-key-pinning - Create a Development or Production Build
- Development Build (Recommended for Testing)
This bundles the native module into your app so you can debug on a simulator or device.npx expo run:ios npx expo run:android - Alternatively, create a Production Build
(Or use the newer EAS Build system.)npx expo build:ios npx expo build:android
- Development Build (Recommended for Testing)
- (Optional) Disable Network Inspector on iOS Dev Client
If you’re testing pinning on an iOS development build and notice that requests are not going through or are being intercepted, you might need to disableexpo-dev-client’s network inspector (which can interfere with pinning).- Install the Build Properties plugin:
npx expo install expo-build-properties - In your
app.jsonorapp.config.js, add:{ "expo": { "plugins": [ [ "expo-build-properties", { "ios": { "networkInspector": false } } ] ] } } - Run:
npx expo prebuild - After that, rebuild your app (e.g.,
npx expo run:ios) so these settings take effect.
- Install the Build Properties plugin:
- Run Your App
Once your custom build is installed on the simulator or device, you can proceed with the pinning steps below.
2. Retrieving the Pins from SSL Labs
A crucial part of SSL pinning is embedding the server’s public key hashes in your code. One easy way to retrieve these hashes is via SSL Labs:
- Navigate to SSL Labs
Visit https://www.ssllabs.com/ssltest/ and enter your domain. - Wait for the Scan
After the scan completes, you’ll see details about your server’s certificates. For getting the 3 most important pins for your app, go to the Certification Paths and copy the pins to store in your project later, get all 3 of them or at least 2
- Grab the Public Key Info
Look for a field that displays the SHA-256 public key hashes. These strings typically look something likemEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=. You’ll use these in your code. - Use Multiple Pins
Best practice is to include at least two pins per domain (one primary and one backup). On iOS, the native library (TrustKit) enforces a minimum of two pins for each domain you configure.
3. Setting Up the Code
Now that you have your base64-encoded SHA-256 public key hashes, it’s time to embed them in your app:
- Import and Initialize Pinning
Add the following to your main application file (e.g.,App.jsor a top-level component):import { initializeSslPinning } from "react-native-ssl-public-key-pinning"; async function setupPinning() { await initializeSslPinning({ "example.com": { includeSubdomains: true, // Whether to pin subdomains as well publicKeyHashes: [ "mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=", "CLOmM1/OXvSPjw5UOYbAf9GKOxImEp9hhku9W90fHMk=", // Backup pin ], // Optionally set an expiration date (yyyy-MM-dd) for your pins // expirationDate: "2025-12-31", }, // ...add more domains if needed }); } // Call setupPinning() early in your app’s lifecycle setupPinning(); - Use Fetch or Axios as Usual
Since this library hooks into React Native’s Networking layer, any standard network requests toexample.comwill now be validated against these pins. - Listen for Errors (Optional)
import { addSslPinningErrorListener } from "react-native-ssl-public-key-pinning"; const subscription = addSslPinningErrorListener((error) => { // Error details about domain or pin mismatch console.warn("Pinning error on domain:", error.serverHostname); }); // Clean up when unmounting or reloading // subscription.remove();
4. Testing with Charles Proxy
Finally, confirm your SSL pinning by attempting a MITM attack using Charles Proxy:
- Install and Configure Charles
- Download Charles on your computer from charlesproxy.com.
- Follow their setup documentation to configure your iOS or Android device (or simulator) to route traffic through Charles.
- Attempt to Intercept Traffic
- With Charles running, try making requests from your app (which has pinning enabled) to the pinned domain.
- If your pinning is correct, Charles will not be able to decrypt or intercept those requests. Typically, you’ll see an error in Charles or the request will fail in your app if Charles tries to present a fake certificate.
- Validate Success
- If you see requests failing when you attempt to intercept them, that means your pinning is working correctly.
- If you do see decrypted responses in Charles or experience no error, double-check that your domain name matches your pinned configuration, and confirm you have the correct SHA-256 hash(es) from SSL Labs.





