## Intro

React Native networking problems often masquerade as app bugs when the real culprits are DNS, ports, routing, firewalls, proxies, or TLS. The fastest path to a fix is a disciplined, measurable approach: start with a narrow health check, verify from the device runtime you actually use (Android emulator, iOS simulator, or physical device), then expand only after you have a green baseline.

This guide is practitioner-focused. You will inventory versions and topology, apply safe and reversible configuration, run concrete diagnostics with expected results, map common failures to fixes, and use a checklist you can repeat across projects. The same playbook works whether you build with bare React Native or Expo, and whether your backend is REST, GraphQL, or another API.

## Version and Environment Inventory

Before you change settings, capture the exact environment. This avoids chasing differences between machines and makes issues reproducible.

### Record app and tooling versions

node -v
npm -v
# or if using yarn or pnpm
yarn -v
pnpm -v 
 npx react-native info 
 adb version
emulator -version 
 xcodebuild -version
xcrun simctl list devices 

### Capture device/runtime context

- Are you using Android Emulator, iOS Simulator, Expo Go, or a physical device?

- Is the device on the same LAN as your development machine?

- Is a VPN or corporate proxy enabled?

### Capture backend endpoints and ports

- Exact URLs including scheme and port, for example:

- https://api.example.local:8443/healthz

- http://192.168.1.50:3000/ping

- Any DNS search domains or split-horizon DNS you rely on.

### Snapshot network configuration on your development machine

 macOS:

scutil --dns
networksetup -getwebproxy Wi-Fi
ifconfig 
 Linux:

resolvectl status || systemd-resolve --status
ip addr
ip route 
 Windows (PowerShell):

Get-DnsClientServerAddress
Get-NetIPConfiguration
netsh winhttp show proxy 
 Pick one simple health endpoint and one runtime (for example, Android emulator). Aim to get a successful 200 OK HEAD or GET request first. Expand to other runtimes only after that is verified. A narrow, measurable pilot is easier to inspect locally and avoids compounding variables.

### Choose a minimal pilot target

## Safe Configuration Path

Use the correct hostnames and reversible settings. The table below lists the right way to reach your development machine from different runtimes.

<div class="my-stack-md overflow-x-auto">
<table class="w-full min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Runtime</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Host to reach your dev machine</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Optional port mapping</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Notes</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Android Emulator (AVD)</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://10.0.2.2</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">adb reverse tcp:3000 tcp:3000</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">10.0.2.2 maps to host loopback. Reverse mapping lets a physical device hit host ports.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Android Device (USB)</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://127.0.0.1</code> with adb reverse, or <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://&lt;LAN_IP&gt;</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">adb reverse tcp:3000 tcp:3000</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Without reverse, use your machine LAN IP on same network.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">iOS Simulator</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://localhost</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Not required</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Simulator shares host network stack.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">iOS Device (Wi-Fi)</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://&lt;LAN_IP&gt;</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Not available</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Ensure device and machine are on same LAN.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Expo Go (Android)</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://10.0.2.2</code> (emulator) or <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">http://&lt;LAN_IP&gt;</code> (device)</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">adb reverse</code> optional</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Expo Go uses device networking; the same rules apply.</td></tr></tbody>
</table>
</div>

### Key safe patterns (development only)

Android dev port mapping with adb reverse

adb reverse tcp:3000 tcp:3000
# remove mappings when done
adb reverse --remove-all 
 If your dev API is http (not https), add a debug-only network security config and reference it from debug manifest. Example: android/app/src/debug/res/xml/network_security_config.xml

Dev-only cleartext allowance (Android)

<network-security-config>
 <domain-config cleartextTrafficPermitted="true">
 <domain includeSubdomains="true">192.168.1.50</domain>
 </domain-config>
</network-security-config> 
 In android/app/src/debug/AndroidManifest.xml :

<application
 android:networkSecurityConfig="@xml/network_security_config"
 ...>
</application> 
 Remove for release builds.

For http endpoints during development only, add to ios/<App>/Info.plist under a debug configuration:

Dev-only ATS relaxation (iOS)

<key>NSAppTransportSecurity</key>
<dict>
 <key>NSAllowsArbitraryLoads</key>
 <true/>
</dict> 
 Remove before shipping. Prefer HTTPS end to end.

Prefer trusting a local CA certificate at the OS level. On Android 7+, if you need the app to trust user-installed CAs during dev, declare a debug-only network security config that trusts user certificates. On iOS, install the CA profile and mark it as trusted for SSL.

Self-signed TLS in development

CORS is a browser security model; React Native network stacks do not enforce it the same way. If your server blocks requests due to Origin or custom headers, it is a server-side policy, not a client runtime limitation.

Do not assume CORS in React Native

Some mobile networks prefer IPv6. Use explicit hostnames and ensure your DNS returns records your service supports. If connecting by literal IP, prefer IPv4 addresses when your service is IPv4-only.

IPv4 vs IPv6

## Verification and Diagnostics

Follow these steps in order. Stop when you get a green result; then widen scope to the next runtime or endpoint.

### 1. Confirm app-level connectivity with NetInfo

import NetInfo from "@react-native-community/netinfo\);n
NetInfo.fetch().then(state => {
 console.log("isConnected:" state.isConnected);
 console.log("isInternetReachable:" state.isInternetReachable);
}); 
 Expected result: isConnected true and isInternetReachable true on a working network. If false, resolve device connectivity (Wi-Fi, cellular, captive portal) before continuing.

### 2. Test a simple HEAD request from the app

Use a tiny endpoint like /healthz that does no heavy work.

// Example: robust fetch with timeout via AbortController
const withTimeout = (ms, controller) => setTimeout(() => controller.abort(), ms);

async function checkHealth(url) {
 const controller = new AbortController();
 const timer = withTimeout(5000, controller);
 try {
 const res = await fetch(url, { method: 'HEAD', signal: controller.signal });
 console.log('status', res.status);
 return res.ok;
 } catch (e) {
 console.log('error', String(e));
 return false;
 } finally {
 clearTimeout(timer);
 }
}

// Choose the right host: see table above
checkHealth('http://10.0.2.2:3000/healthz'); 
 Expected result: status 200. If error includes TypeError: Network request failed , continue below.

### 3. Isolate DNS vs transport

From your development machine, resolve and connect to the exact hostname used in the app.

macOS/Linux:

dig +short api.example.local
nc -vz api.example.local 3000 
 Windows (PowerShell):

Resolve-DnsName api.example.local
Test-NetConnection api.example.local -Port 3000 
 Expected result: DNS returns at least one address; port test succeeds. If DNS fails but IP works, update your app config to use a reachable host (LAN IP for dev) or fix DNS split-horizon rules.

### 4. Verify port reachability from device runtime

- Android emulator inherits host reachability to 10.0.2.2. If you connect to a LAN IP instead, ensure the host firewall allows inbound on that port.

- Physical Android device: prefer adb reverse for local development, or open firewall to your LAN IP and test via http://<LAN_IP> .

- iOS simulator uses host networking; localhost works.

- Physical iOS device: ensure same Wi-Fi and open firewall.

Host firewall checks

macOS (PF):

sudo pfctl -s rules
lsof -iTCP -sTCP:LISTEN -P | grep 3000 
 Linux (UFW or iptables):

sudo ufw status verbose
sudo ss -ltnp | grep :3000 
 Windows:

Get-NetFirewallProfile
Get-NetFirewallRule | Select-String -Pattern 3000
netstat -ano | findstr :3000 
 Expected result: your server process is listening, and firewall permits inbound on the correct interface.

### 5. Check routing and proxies

If a system proxy is configured, mobile runtimes may honor it.

macOS:

networksetup -getwebproxy Wi-Fi 
 Windows:

netsh winhttp show proxy 
 Linux:

env | egrep 'https?_proxy|HTTPS?_PROXY' 
 Expected result: either no unintended proxy, or proxy rules that allow your requests.

### 6. Observe logs on failures

Android:

adb logcat | egrep -i 'okhttp|ssl|connect|react|fatal' 
 Look for SSLHandshakeException , UnknownHostException , or ConnectException .

iOS: open Console.app, filter on your process, or view Xcode logs. Look for NSURLErrorDomain codes such as -1003 (cannot find host), -1200 (TLS failure), -1004 (cannot connect to host), -1001 (timeout).

### 7. TLS verification

Use the full https URL in the app and confirm the certificate chain is valid for the device. If using a local CA for dev, ensure the CA is trusted by the device and, on Android, allowed by your debug network security config.

### 8. Retry and backoff (client resilience)

Add bounded retries for transient network issues.

async function retry(fn, retries = 2, base = 300) {
 let attempt = 0;
 while (true) {
 try { return await fn(); }
 catch (e) {
 if (attempt++ >= retries) throw e;
 const delay = base * Math.pow(2, attempt - 1);
 await new Promise(r => setTimeout(r, delay));
 }
 }
}

await retry(() => checkHealth('http://10.0.2.2:3000/healthz')); 
 Expected result: transient failures recover within a few attempts. Persistent failures should still surface clearly.

## Failure Modes and Recovery

Use this mapping to move quickly from symptom to fix.

<div class="my-stack-md overflow-x-auto">
<table class="w-full min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Symptom or error</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Probable cause</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Targeted fix</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">ERR_NAME_NOT_RESOLVED</code>, <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">UnknownHostException</code>, <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">NSURLErrorDomain -1003</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">DNS does not resolve from device runtime</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Use the correct host for runtime (10.0.2.2, localhost, or LAN IP). Fix DNS or use LAN IP in dev.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">ECONNREFUSED</code>, cannot connect, connection refused</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Port closed or server not listening</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Start server, verify listen address 0.0.0.0, open firewall, confirm port with netstat/ss.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Timeout or <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">NSURLErrorDomain -1001</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Firewall, proxy, or routing drop; server slow</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Bypass proxy, open firewall, test nc/Test-NetConnection, add server health endpoint.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">SSLHandshakeException</code>, <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">NSURLErrorDomain -1200</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Certificate invalid or untrusted CA</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Use HTTPS with valid cert, trust local CA on device, add debug-only trust config on Android.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Works in simulator, fails on device</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Wrong host (localhost) or LAN isolation</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Use LAN IP on devices; ensure same Wi-Fi; consider adb reverse for Android USB.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">IPv6-only network issues</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Service only reachable via IPv4</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Ensure DNS returns AAAA or use dual-stack; avoid hardcoded IPv4-literal URLs when on IPv6-only networks.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">POST/PUT fail, GET works</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Server rejects headers/body size</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Inspect server logs; adjust Content-Type, body, and server limits.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Belief that CORS blocks requests</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">RN not enforcing browser CORS</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Fix server policy if it blocks Origin; it is not a React Native client restriction.</td></tr></tbody>
</table>
</div>

### Recovery and rollback

- Remove adb reverse mappings when done:

adb reverse --remove-all 

- Revert debug-only network relaxations (Android manifest, iOS ATS) before any release build.

- If you changed OS firewall rules, document and revert to the team baseline once development testing is complete.

- If you installed a local CA on devices for dev, remove it from devices not used for development.

## Practical examples

### Switch localhost to the right host per runtime

 Android emulator:

const API = 'http://10.0.2.2:3000'; 
 iOS simulator:

const API = 'http://localhost:3000'; 
 Physical device on LAN (replace with actual IP):

const API = 'http://192.168.1.50:3000'; 

### File upload with explicit timeout and error surface

 async function uploadImage(uri) {
 const data = new FormData();
 data.append('file', { uri, type: 'image/jpeg', name: 'photo.jpg' });
 const controller = new AbortController();
 const t = setTimeout(() => controller.abort(), 15000);
 try {
 const res = await fetch(`${API}/upload`, {
 method: 'POST',
 body: data,
 headers: { 'Accept': 'application/json' },
 signal: controller.signal,
 });
 if (!res.ok) throw new Error(`Upload failed ${res.status}`);
 return await res.json();
 } finally {
 clearTimeout(t);
 }
} 
 Expected result: 200 or 201 response with JSON body; on abort, surface a clear error so users can retry.

### Connectivity guardrail before calling APIs

async function guardedFetch(url, init) {
 const s = await NetInfo.fetch();
 if (!s.isConnected || !s.isInternetReachable) {
 throw new Error('No internet connection');
 }
 return fetch(url, init);
} 
 This avoids spamming your backend when the device is offline.

## Quick command library

Use these commands to test DNS and port reachability from your development machine.

<div class="my-stack-md overflow-x-auto">
<table class="w-full min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">OS</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">DNS check</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Port check</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Notes</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">macOS</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">dig +short host.example</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">nc -vz host.example 3000</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Also use <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">scutil --dns</code> for resolver details.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Linux</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">getent hosts host.example</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">nc -vz host.example 3000</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">resolvectl status</code> for per-link DNS.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Windows</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">Resolve-DnsName host.example</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">Test-NetConnection host.example -Port 3000</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Add <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">-InformationLevel Detailed</code> for more output.</td></tr></tbody>
</table>
</div>

### Expected results

- DNS check returns at least one IP.

- Port check reports succeeded; otherwise, investigate firewall, listen address, or routing.

## Operations Checklist

Use this repeatable list on every React Native networking issue.

- Inventory

- Record RN, OS, device/emulator, and backend URLs and ports.

- Note VPN/proxy and firewall status.

- Pick a pilot

- Choose one runtime (e.g., Android emulator) and one health endpoint.

- Configure safely

- Use correct host (10.0.2.2, localhost, or LAN IP).

- Optionally use adb reverse for Android development.

- Add dev-only network relaxations if absolutely required; document them.

- Verify stepwise

- Check NetInfo connectivity from the app.

- Make a HEAD request with a 5s timeout; expect 200.

- From your machine, resolve DNS and test the port.

- Confirm server is listening and firewall allows inbound.

- Observe logs

- Android: adb logcat for okhttp/ssl/connect errors.

- iOS: Xcode or Console for NSURLErrorDomain codes.

- Fix by failure mode

- Wrong host: use runtime-appropriate host.

- Port closed: start server, listen on 0.0.0.0, open firewall.

- DNS breaks: use LAN IP in dev or fix DNS.

- TLS breaks: trust dev CA or use valid certs; remove relaxations later.

- Rollback

- Remove adb reverse mappings.

- Remove debug-only network exceptions.

- Revert firewall changes to team baseline.

## Conclusion

React Native networking becomes predictable when you treat it as a sequence of observable steps: inventory, pick a narrow pilot, apply safe and reversible configuration, verify from the actual runtime, and expand only after you get a clean green check. With correct host selection, clear DNS and port tests, and focused logging, you can isolate issues in minutes instead of days. Keep development relaxations strictly debug-only, document every change, and roll them back as soon as your checks pass. This approach scales from individual developers to startup teams and consultant engagements while keeping risk low and outcomes measurable.