Sebastian Weber Frontend developer from Germany. Love CSS and Vue.js. Starting to love React.

How to debug encrypted network traffic in React Native

14 min read 4177

Debug Encrypted Network Traffic React Native

Although it has the potential to provide the best UX for users, developing iOS and Android apps natively usually isn’t an option for JavaScript developers. This is where React Native comes into play.

React Native application code can be analyzed with the inspector, profiler, and remote debugger, all accessed by the in-app development menu. You can combine this with the stand-alone React Developer Tools. In contrast to native development, however, this approach has its limits with regards to debugging network communication.

This article covers additional tools for React Native that enable debugging capabilities the shipped React Native Developer Tools don’t offer. The focus is on inspecting and rewriting network traffic between your app and server.

At the end of this article, you will have learned that debugging network traffic for Android apps has its limits depending on the selected development approaches, such as Expo managed workflow, ejecting from the Expo managed approach, or React Native CLI.

This article describes the handy local proxy tools Charles Proxy, HTTP Toolkit, and Proxyman. For debugging encrypted network traffic for iOS, I cover Proxyman and Charles Proxy, and for Android, HTTP Toolkit and Proxyman (I skip Charles Proxy even though this is supported).

My goal is not to cover every possible use case with every tool, but to show you different scenarios with different tools in order not to drag out the article unnecessarily. I only cover how to debug the traffic of your connected iOS tool with Proxyman, for example, and not with Charles Proxy (though it’s possible).

Additionally, some debugging use cases are only possible on real devices; thus, I’ll show you two handy tools for iOS and Android development to mirror the device screens on your machine. This is a lifesaver in a remote pair programming session.

React Native environments and their network debugging capabilities

There are several ways to develop a React Native app. The most common options are:

  1. The default method recommended by Facebook with the React Native CLI
  2. A managed workflow with Expo, which represents the lowest initial hurdle
  3. A hybrid approach with native code components and components written in React Native

There are many differences between these three approaches, but the most important distinction in terms debugging network traffic is whether you have access to the native Android code or not.

As you will see later, to enable all capabilities of network debugging, you need to define some security settings in native Android code. This is possible with options 1 and 3, but not with 2 — except if you eject from the managed workflow.

We made a custom demo for .
No really. Click here to check it out.

Your ability to inspect HTTPS network calls comes down to whether it’s possible to establish an HTTP(S) proxy in your development setup with a local proxy tool like Charles Proxy or Proxyman. They install SSL certificates that enable you to view encrypted HTTPS contents. For Android in particular, it boils down to whether you can customize the native network security configuration.

Below is an overview of whether encrypted requests and responses can be inspected for each of the three options reviewed above:

  1. iOS, Android
  2. iOS; but if ejected from managed workflow, Android as well
  3. iOS, Android

React Native development environment

For those readers who are new to React Native, the next section will give you a quick overview on how to set up a “pure” React Native environment (options 1 and 2 above). I do not cover the hybrid approach (3) because this would go beyond the scope of the article.

Managed workflow with Expo

Expo is a great way to get a basic React Native app up and running in a pinch. If you do not plan to work on a hybrid app project, Expo is a good choice. Make sure you can implement all your requirements with the existing component libraries.

$ npm install --global expo-cli [1] install expo cli globally
$ expo init react-native-sandbox [1] create a project 

The second command opens up an interactive menu to select from a template, with or without TypeScript.

Currently, the Expo team creates a project with Yarn as the package manager, so use the following command from the project’s root folder to start the Metro bundler:

$ yarn start

The Metro bundler should open up in your default browser.

Metro Bundler Open Default Browser
Expo scores with a good developer experience and intuitive workflow.

As you can see, Expo scores high marks with a good developer experience and an intuitive workflow. Just open your camera app on your mobile device and scan the QR code and, simsalabim, the app opens up on your device.

If Metro crashes, it might help to reinstall Watchman, which is used under the hood.

$ brew reinstall watchman

React Native CLI

It’s fair to say that Expo’s managed approach might not fit your use case — say, if you wanted to incorporate native modules. Take a look Expo’s documentation on its limitations. The following video gives a good overview when to use Expo or the React Native CLI:

Before you can create a project, you need to set up your dev environment for iOS and Android. You’ll need to invest more time in the initial setup compared to Expo, but once it’s complete, the following commands will produce a working initial project.

For Android, you either need to have an emulator up and running or connect a device. iOS is a little easier to cater for; the simulator opens automatically. The next section goes into more detail about how to set this up.

$ npx react-native init rnCliPlayground # init project
$ cd rnCliPlayground && npx react-native run-ios # start iOS app
$ cd rnCliPlayground && npx react-native run-android # start Android app 

In contrast to Expo, my experience is that more can go wrong during setup, and it can take longer for iOS and Android to run. On the other hand, the documentation from the React Native team is helpful. That said, though, Expo works as well as always out of the box.

Example project

The examples in the remainder of the article always refer to a very simple Expo project; I just generated the project as described above. The only change is to override App.js with the following code, which fetches a JSON array of movies:

import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default function App() {
  const [movies, setMovies] = useState([])
  const [loading, setLoading] = useState(false);
  useEffect(() => {
      setLoading(true);
      const getMoviesFromApi = () => {
        return fetch('https://reactnative.dev/movies.json')
          .then((response) => {
            if (response.status < 400) {
              return response.json()
            }
            else {
              return {
                movies: []
              };
            }
          })
          .then((json) => {
            return json.movies;
          })
          .catch((error) => {
            return {
              movies: []
            };
          });
      };
      getMoviesFromApi().then(response => {
        setMovies(response)
        setLoading(false);
      });
  }, [])
  return (
    <View style={styles.container}>
      <StatusBar style="auto" />
      {loading && <Text>Loading...</Text>}
      {!loading && movies.length > 0 && movies.map(movie => 
        <Text key={movie.id}>{movie.title}</Text>
      )}
      {!loading && movies.length === 0 && <Text>no movies found</Text>}      
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Set up iOS Simulator and Android Emulator

For the purposes of this article, you’ll need to use either iOS Simulator/Android Emulator or real devices. Therefore, you need to set up Xcode and Android Studio, respectively. From the iOS perspective, you only need the capability to open Simulator, whereas with Android, you need to customize security configurations in code.

Xcode

You can download and install Xcode either from developer.apple.com or the App Store. From the Expo CLI, you can press i or click the Run on iOS simulator link in the Expo browser interface. You can press ⌘ d in Simulator to open up the in-app developer menu.

Xcode Expo CLI Run iOS Simulator Menu
Expo app opened in Simulator.

Android Studio

After you’ve installed Android Studio, you have to properly configure it to open your React Native project in Android Emulator. Start the Metro bundler of your Expo project with the following command:

$ yarn start

The managed workflow starts Metro and opens the developer tools inside the browser and in the terminal. In the terminal, you can press a to open up your Expo app in Android Emulator. You can do this either from the Expo CLI or from the browser view by clicking on Run on Android device/emulator.

Expo Metro Bundler Android Device Run
Output from the Expo CLI.

You can open the in-app developer menu with ⌘ m.

Android App Developer Menu Expo
Expo app opened in Android Emulator.

Tools that foster remote collaboration

There are development use cases where working with the iOS Simulator or Android Emulator is not possible, e.g., debugging your iOS app with different network settings or airplane mode. Therefore, you need to work with your actual device.

This section presents handy tools to work with real devices to facilitate debugging real devices. There are also ways to mirror the screen of your actual device to your development machine, which improves the developer experience in remote pair programming scenarios.

scrcpy for screen mirroring and remote control

scrcpy is an awesome utility for Android development that is available for all major operating systems. It mirrors the screen of a device connected via USB cable to the developer machine.

What’s especially great is that the mobile device can also be remote-controlled by the tool on your development machine instead of using gestures on a device. It’s especially handy if you share your screen in a video conference so that peers can see the mouse cursor to get an idea how you interact with the mirrored device.

Installation for macOS is straightforward with Homebrew. As a requirement, you need ADB (Android Debug Bridge) up and running. In addition, you have to enable USB debugging on your Android device.

$ brew install scrcpy
$ brew install --cask android-platform-tools [1] install adb

In order to get scrcpy to work with a React Native app running on your device, you need to establish a reverse proxy with adb reverse.

$ adb reverse tcp:8081 tcp:8081

Quicktime for screen mirroring

When you connect your iOS device via USB cable to your Mac, you can leverage QuickTime Player to mirror your device screen. You first have to trust your connected Mac, then open QuickTime Player and start a new movie recording (⌥ ⌘ N). Next to the red recording button, open the dropdown and select your connected device from the camera input. That’s it.

The screenshot below shows how I mirror my Expo app from my iPhone connected via USB cable to my MacBook.

Expo App Mirror iPhone USB Cable
QuickTime can be used to mirror your device’s screen.

Inspecting network traffic with Proxyman

Proxyman is a macOS-only proxy tool to intercept, view, debug, and rewrite the HTTP(S) network traffic of iOS and Android apps.

Using with Expo app running in iOS Simulator and on an iOS device

In order to inspect encrypted HTTPS messages, you have to install Proxyman CA Certificate on your machine. Then, just open the certificate dialog by selecting Certificate > Install Certificate on this Mac… from the main menu. Click on the install button in the Automatic tab.

Proxyman CA Certificate Mac Install Guide
The root certificate is required to inspect HTTPS traffic

This is a prerequisite for using Proxyman with iOS simulators and devices.

Set up iOS Simulator

You’ll need to perform a one-time setup to install and trust certificates on all iOS simulators. Therefore, select the setup dialog from the main menu by selecting Certificate > Install Certificate on iOS > Simulators…. Click on the buttons of step 2 and 3 and you’re good to go.

iOS Simulator Setup Certificates
Installation of certificate on all iOS simulators.

What I really admire is this tool’s usability, especially the very helpful interactive checklists for the different scenarios.

Set up iOS devices

This step is similar but needs a little additional work on the actual device. You need to connect your iOS device to your Mac via USB cable. Make sure it appears in the Finder app before you proceed. Next, open the certificate dialog from the main menu (Certificate > Install Certificate on iOS > Physical Devices…).

iOS Device Setup Mac USB Cable Connect
Installation of certificate for a connected iOS device and proxy setup.

Just do what the dialog urges you to do. Proxyman uses port 9090.

View network traffic

Now, you can start to view the network traffic. Open up the Metro bundler of your Expo app and press i for a simulator or scan the QR code with the camera app on your iOS device connected by USB cable.

You should see the network traffic of the app, which invokes a GET request every time you reload from the Expo development menu by pressing ⌘ d on the iOS simulator or by shaking your iOS device.

App Network Traffic Expo Get Request
Initially the GET request is encrypted.

In order to inspect the response body, you need to enable SSL proxying by clicking on the button Enable all domains from “Expo”. The following screenshot shows the inspected response sent by the server after invoking a GET call from the iOS simulator.

Server Inspected GET Response iOS Simulator

Deactivate caching

In the example request, the server answers with a 304 status code because the response has already been cached by the client. To change this behavior and get a 200 status code with the movie content, you can deactivate caching via Tools > No Caching.

Deactivate Caching 200 Status Code
Deactivating caching is helpful for debugging purposes.

Stub network calls

In order to play out different use cases, it is useful to fake network calls by creating stubs. In this example, we’d like to change the response body to consist of one movie instead of four.

Proxyman provides different options for that. Load a local JSON file as a stub with the Map Local Tool whenever the route matches the concrete URL https://reactnative.de/movies.json. This is the file you want to load:

// stub.json
{
  "title": "The Basics - Networking",
  "description": "Your app fetched this from a remote endpoint!",
  "movies": [
    {
      "id": "1899",
      "title": "Dumb and Dumber",
      "releaseYear": "1994"
    }
  ]
}

You can define multiple stubs and enable or disable them as you like.

Define Disable Multiple Stubs Map Local

Whenever you press ⌘ r in the Expo CLI, a new request is invoked and the content of the JSON file is returned instead of the original server response.

Expo CLI New Request JSON Content Return

Breakpoints

The concept of Breakpoints is a powerful tool to debug the correct behavior between your app and the server. The following example shows an example breakpoint for the concrete URL https://reactnative.de/movies.json.

Breakpoint Rules Concrete URL
Pause network communication to manually change the response.

With that in place, every time this rule is matched, the network call is paused. As you can see in the next screenshot, you then have the ability to manually define the response body. In this case, you’d change the response body to consist only of one movie. By the way, you can see that the loading logic works correctly because the user sees the loading label during the loading process.

Manual Define Response Body Loading Label View

Using Proxyman with Android

To use Proxyman with Android, you need to override the security settings in Android Studio, as explained in the official documentation.

The nice thing with Proxyman is the interactive documentation. To use it with Android, select Certificate Menu > Install Certificate on Android > Emulator… from the main menu. In the opening dialog, you get the code to use as a security setting to enable SSL proxying.

Proxyman Install Certificate Android Emulator
Installation of Android certificates takes place in an opening terminal window.

Because we have Expo’s managed workflow, we do not have the chance to insert the above provided config code. That’s why we have SSL handshake problems and cannot see the response body.

SSL Handshake Problems Expo App Network Call
We cannot view the response of our Expo’s app network call.

In order to change this, you need to eject from the managed workflow. If you want to stick to this workflow, you just can eject for the debugging session and revert your Git changes after that.

In my current work project, we are developing on a hybrid approach that consists of a natively developed framework with components partly written in React Native. The above security configuration is defined for non-production builds.

I have refrained from this in this article. I describe how to debug network calls for Android with the next tool.

Inspecting network traffic with HTTP Toolkit

HTTP Toolkit is a nice looking and intuitive tool to intercept, view, and debug HTTP(S) endpoints. You can stub requests and responses to rewrite or redirect your app’s network traffic with your server, or inject errors. It’s available for macOS, Windows, and Linux.

During the preparation of this article, HTTP Toolkit has been the tool working best out of the box — no configuration was necessary. The only restriction is that, at the time of writing, it doesn’t support iOS.

Using HTTP Toolkit with an Expo app running in Android Emulator

The first step is to open up an Android emulator from Android Studio. Next, start the Metro bundler of your Expo app. Press a to launch the React Native app in the emulator.

I experienced more problems with Android emulators than with iOS simulators. If you cannot get the Expo app running, the easiest way is to remove the Android emulator and create it again from the Android Virtual Device Manager.

Next, start HTTP Toolkit and click on the option Android device connected via ADB within the Intercept section.

HTTP Toolkit Android Device Connected ADB Intercept
The first step is to intercept network traffic.

A dialog should pop up on your emulator to establish a VPN connection. Click on OK.

HTTP Toolkit Dialog Box VPN Connection
HTTP Toolkit wants to establish a VPN connection.

If everything worked out right, then you should see a success message.

HTTP Toolkit VPN Connection Success Message
HTTP Toolkit shows a success message after the VPN connection has been established.

View network traffic

Now you should be up and running. As a first step, switch to the View section in order to monitor the network traffic of your Expo app with the server. In this case, you’re interested in the reactnative.dev endpoint, which you can filter for in the lower part of the UI.

Network Traffic Expo App View
Network traffic of your Expo app is fully viewable.

As you can see, without any intervention, the response body of the GET call is decrypted.

During the preparation of this article, I could reliably use HTTP Toolkit to view HTTPS messages with the managed Expo project.

Rewrite network calls

You can define rewrite rules in the Mock. Create a rule to intercept every GET request with the URL https://reactnative.dev/movies.json and create a custom response.

Mock Rewrite Rules Intercept GET

After reloading the app, you can see that the GET request (status code 304) has been paused, and you’ll have a chance to change the response body. While the response is paused, you can debug your app and check whether the loading state is working correct. Change the response to contain only one movie and a status code of 200.

App Reload Network Pause Breakpoint
The network call is paused by a breakpoint due to the defined rule

After a click on the Resume button, the app only shows one movie due to the fact that the mocked network call returned with status code 200 and the custom JSON object.

App Reload Network Pause Breakpoint
After the breakpoint was resumed, the network call returns the custom response

Inspecting network traffic with Charles Proxy

Charles Proxy is a widely used local proxy tool. It’s available for macOS, Windows, and Linux, and it supports iOS and Android. In this article, I describe how to set it up with iOS and skip the Android part since I describe another option with HTTP Toolkit for Android inspection later in this article.

View SSL-encrypted content

You can use Charles Proxy to intercept, view, and manipulate HTTPS contents. This includes requests, responses, and the HTTP headers. It is especially useful for monitoring your React Native app’s network traffic in the simulator/emulator and on your connected physical device as well.

In my current project, it has allowed me to debug error handling due to server errors. As an example, you can block entire requests to force an error condition.

Charles Proxy is not exclusive for React Native development. There exist many installation guides. Here, I’ll describe how to set it up on a Mac for an Expo project.

After downloading and installing Charles Proxy, you have to grant privileges.

Charles Proxy Grant Privilege
Granting privileges.

Open up Charles Proxy and you’ll see network traffic popping up in the sequence view. Open this in the browser and filter for jsonplaceholder.typicode.com. Click on the entry and select Contents and Raw. As you can see from the next screenshot, the network content cannot be displayed correctly because it is SSL-encrypted.

Network Content SSL Encrypted
Without certificates, you cannot view SSL-encrypted content.

To change this, you have to open the Proxy Settings (Proxy > Proxy Settings…, or ⇧⌘L) and add an entry to the SSL entry section.

SSL Proxy Settings Entry
You need to set up proxy settings to view SSL-encrypted content.

In this example, I only set it up for jsonplaceholder.typicode.com (host jsonplaceholder.typicode.com and port *), but you can also define host * and port *. After ticking the checkbox, reload your page and click on the new entry again.

In Chrome, you’ll get a warning that your connection is not private. You need to proceed.

Chrome Warning Connection Not Private
Chrome warns you that the connection may no longer be private.

Reload your browser page again, click again on the request, and you can see the content unencrypted.

Chrome Browser Reload Unencrypted Content
With proxy settings, you can view SSL encrypted content

Using with the Expo app running in iOS simulators

Right now, if you run your React Native app in the iOS simulator, Charles Proxy does not show the network traffic. We need to install Charles root certificates.

Install the certificate for iOS simulators (Help > SSL Proxying > Install Charles Root Certificate in iOS Simulators). Make sure the macOS proxy is selected as well (Proxy > MacOS proxy) and the certificate is trusted in the iOS simulator (Settings app > General > About > Certificate Trust Settings > Activate Charles Proxy CA).

Charles Root Certificate Trust Settings iOS Simulator
The root certificate has to be trusted in the iOS simulator.

Block requests

For debugging error scenarios, it might be useful to add requests to a block list (right-click on Request > Block list).

In the simple example project, I have blocked the reactnative.dev GET call. As you can see from the screenshot, it showed that such errors are not handled correctly and the user has to make do with a blank page.

React Native Dev GET Call Blocked
We do not handle our network error correctly.

More debugging features

Charles Proxy is pretty powerful. There are many more features (rewriting network calls with map local/remote, breakpoints, or deactivating caching) that I skipped here because I have already showed it in the Proxyman section. It can also be used for debugging Android apps. As with Proxyman, you need to eject from the managed Expo workflow to establish the right security configuration.

Conclusion

There are powerful tools available to React Native developers that extend the shipped dev tools for inspecting and even rewriting network traffic. This is useful for debugging complex client-to-server interactions. This article gives an overview of proxy tools, and there are quite a few choices available — I haven’t even looked at Fiddler yet.

Using proxy tools for iOS development is much easier to configure and less error-prone. In contrast, I had to delete and recreate Android emulators a lot during the preparation of this article. For full-fledged Android debugging, you might not be able to use Expo’s managed workflow because you may need to override Android’s security settings in Android Studio.

From my experience, this is a required step for Proxyman and Charles. With HTTP Toolkit, however, I could view SSL-encrypted network calls out of the box without any extra configuration or ejection from Expo’s managed workflow.

But even if you have to eject from Expo’s workflow, then there’s a solution even for this use case. Revert your Git changes after your debug session is done, and you can continue with your development approach of choice. One last word: make sure you do not use the proxy setup for your production builds.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Sebastian Weber Frontend developer from Germany. Love CSS and Vue.js. Starting to love React.

Leave a Reply