DEV Community

Ray Ch
Ray Ch

Posted on

Fixing "Network Error" in React Native When Connecting to QA Servers with HTTPS

You're developing a React Native app using React Native CLI, testing on a physical device, and trying to connect to your QA server (like https://qa.example.com/api/). You keep getting this frustrating error:

Network Error
message: "Network Error"
Enter fullscreen mode Exit fullscreen mode

This happens because your QA server likely uses a self-signed certificate or an untrusted/invalid SSL certificate, and both iOS and Android block these connections by default for security reasons.

Understanding Why This Happens

Modern mobile operating systems (Android 7+ and iOS 9+) enforce strict transport security:

  • Android: Starting from Android 7 (Nougat, API 24), apps no longer trust user-installed certificates by default
  • iOS: App Transport Security (ATS) requires all connections to use HTTPS with valid certificates

When your QA environment uses a self-signed certificate or a certificate from a non-public Certificate Authority (CA), your app refuses the connection - hence the "Network Error".

✅ Verified Solutions

Based on official Android and iOS documentation, here are the proven solutions that actually work:


Solution 1: Android - Network Security Configuration

This is the recommended approach for Android. It allows you to specify which certificates your app should trust without compromising security.

Step 1: Create Network Security Config

Create this file: android/app/src/main/res/xml/network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- Configuration for your QA server -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">qa.example.com</domain>
        <trust-anchors>
            <!-- Trust system certificates -->
            <certificates src="system" />
            <!-- Trust user-installed certificates -->
            <certificates src="user" />
        </trust-anchors>
    </domain-config>
</network-security-config>
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Allows HTTPS connections to qa.example.com and all its subdomains
  • Trusts both system certificates AND user-installed certificates
  • Keeps security enabled (cleartextTrafficPermitted="false")

Step 2: Reference it in AndroidManifest.xml

Update android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yourapp">

    <application
        android:name=".MainApplication"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        ...>
        <!-- Your other application settings -->
    </application>
</manifest>
Enter fullscreen mode Exit fullscreen mode

Alternative: Trust All User Certificates (Easier but Less Secure)

If you want to trust user certificates globally (not recommended for production):

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>
Enter fullscreen mode Exit fullscreen mode

Solution 2: iOS - App Transport Security Exceptions

For iOS, you need to add exceptions to your Info.plist file.

Step 1: Locate Your Info.plist

Find the file: ios/YourAppName/Info.plist

Step 2: Add ATS Exception

Add this configuration to allow insecure connections to your QA server:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>qa.example.com</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Allows insecure connections specifically to qa.example.com
  • Includes all subdomains
  • Keeps ATS enabled for all other domains (secure by default)

Alternative: Disable ATS Globally (Not Recommended)

If you need to disable ATS entirely (only for development):

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
Enter fullscreen mode Exit fullscreen mode

⚠️ Warning: Apple may reject apps that use NSAllowsArbitraryLoads without proper justification.


🔧 Complete Implementation Example

Here's your complete setup for connecting to https://qa.example.com/api/:

1. API Service Configuration

// src/services/apiService.js
import axios from 'axios';

const API_BASE_URL = 'https://qa.example.com/api';

const api = axios.create({
  baseURL: API_BASE_URL,
  timeout: 30000,
  headers: {
    'Content-Type': 'application/json',
  },
});

// Request interceptor
api.interceptors.request.use(
  (config) => {
    console.log('Making request to:', config.url);
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// Response interceptor
api.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    console.error('API Error:', {
      message: error.message,
      code: error.code,
      response: error.response?.data,
      status: error.response?.status
    });
    return Promise.reject(error);
  }
);

export default api;
Enter fullscreen mode Exit fullscreen mode

2. Login Screen Example

// src/screens/LoginScreen.js
import React, { useState } from 'react';
import { View, TextInput, Button, Alert } from 'react-native';
import api from '../services/apiService';

const LoginScreen = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [loading, setLoading] = useState(false);

  const handleLogin = async () => {
    try {
      setLoading(true);

      const response = await api.post('/login', {
        email,
        password
      });

      console.log('Login successful:', response.data);
      Alert.alert('Success', 'Login successful!');

      // Handle successful login (save token, navigate, etc.)

    } catch (error) {
      console.error('Login error:', error);

      Alert.alert(
        'Login Failed',
        error.response?.data?.message || 'Network error occurred'
      );
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <TextInput
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        style={{ borderWidth: 1, marginBottom: 10, padding: 10 }}
      />
      <TextInput
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
        style={{ borderWidth: 1, marginBottom: 10, padding: 10 }}
      />
      <Button 
        title={loading ? "Logging in..." : "Login"} 
        onPress={handleLogin}
        disabled={loading}
      />
    </View>
  );
};

export default LoginScreen;
Enter fullscreen mode Exit fullscreen mode

📱 Rebuild and Test

After making these changes, you must rebuild your app:

# For Android
npx react-native run-android

# For iOS
cd ios && pod install && cd ..
npx react-native run-ios
Enter fullscreen mode Exit fullscreen mode

🔍 Debugging Tips

If you're still experiencing issues, try these debugging steps:

1. Check the Certificate

Verify your QA server's certificate:

# Check certificate details
openssl s_client -connect qa.example.com:443 -showcerts

# Get certificate
echo | openssl s_client -servername qa.example.com -connect qa.example.com:443 2>/dev/null | openssl x509 -text
Enter fullscreen mode Exit fullscreen mode

2. Test the Endpoint

Use curl to test if the endpoint is accessible:

curl -v https://qa.example.com/api/health
Enter fullscreen mode Exit fullscreen mode

3. Enable Detailed Logging

Add detailed error logging to your API calls:

catch (error) {
  console.log('=== Full Error Details ===');
  console.log('Message:', error.message);
  console.log('Code:', error.code);
  console.log('Config:', error.config);
  console.log('Response Data:', error.response?.data);
  console.log('Response Status:', error.response?.status);
  console.log('Response Headers:', error.response?.headers);
  console.log('========================');
}
Enter fullscreen mode Exit fullscreen mode

4. Check Network Connectivity

Ensure your device can reach the QA server:

# On your computer, check if QA server is reachable
ping qa.example.com

# Check if HTTPS port is open
telnet qa.example.com 443
Enter fullscreen mode Exit fullscreen mode

🔐 Security Best Practices

For Development/QA:

DO:

  • Use domain-specific exceptions (not global)
  • Document why each exception is necessary
  • Keep exceptions to minimum required domains
  • Use includeSubdomains carefully

DON'T:

  • Disable ATS/Network Security completely
  • Use these configurations in production builds
  • Trust all certificates globally
  • Commit sensitive certificates to version control

Before Production:

  1. Remove all development exceptions
  2. Get a proper SSL certificate for production (Let's Encrypt, DigiCert, etc.)
  3. Test on real HTTPS endpoints
  4. Use SSL pinning for sensitive apps
// For production, consider using react-native-ssl-pinning
import { fetch } from 'react-native-ssl-pinning';

fetch('https://your-production-api.com/endpoint', {
  method: 'POST',
  sslPinning: {
    certs: ['production-cert'] // Certificate in android/app/src/main/assets/
  },
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ data: 'value' })
});
Enter fullscreen mode Exit fullscreen mode

📦 Alternative: Use SSL Pinning Library

For more robust SSL handling, especially in production, consider using a dedicated library:

npm install react-native-ssl-pinning
Enter fullscreen mode Exit fullscreen mode

Then implement certificate pinning:

import { fetch } from 'react-native-ssl-pinning';

const response = await fetch('https://qa.example.com/api/login', {
  method: 'POST',
  timeoutInterval: 10000,
  sslPinning: {
    certs: ['qa-cert'] // Place qa-cert.cer in android/app/src/main/assets/
  },
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ email, password })
});
Enter fullscreen mode Exit fullscreen mode

🎯 Summary Checklist

For Android:

  • Create network_security_config.xml in res/xml/
  • Add domain configuration with trust anchors
  • Reference config in AndroidManifest.xml
  • Rebuild app with npx react-native run-android

For iOS:

  • Open Info.plist in Xcode or text editor
  • Add NSAppTransportSecurity exceptions
  • Specify your QA domain in NSExceptionDomains
  • Run pod install in ios folder
  • Rebuild app with npx react-native run-ios

For Both:

  • Verify API base URL is correct
  • Test network connectivity to QA server
  • Check device logs for detailed errors
  • Document why exceptions are needed
  • Plan to remove exceptions before production

🔗 Official Documentation References


💡 Common Issues & Solutions

Issue: Still getting "Network Error" after configuration

Solution: Make sure you've rebuilt the app completely. Sometimes a clean build is needed:

# Android
cd android && ./gradlew clean && cd ..
npx react-native run-android

# iOS
cd ios && pod deintegrate && pod install && cd ..
npx react-native run-ios
Enter fullscreen mode Exit fullscreen mode

Issue: Works on Android but not iOS

Solution: iOS requires more specific ATS exceptions. Make sure you've added the domain to NSExceptionDomains and set NSExceptionAllowsInsecureHTTPLoads to true.

Issue: Certificate verification fails

Solution: Install the QA certificate on your device manually:

  • Android: Settings → Security → Install from storage
  • iOS: Email certificate → Install → Trust in Settings

Issue: Works in development but fails in release build

Solution: Release builds may strip debugging configurations. Ensure your Network Security Config and Info.plist exceptions are properly included in release builds.


🎉 Conclusion

The "Network Error" when connecting to HTTPS QA servers in React Native is a security feature, not a bug. By properly configuring Network Security Config for Android and App Transport Security for iOS, you can safely connect to your QA environment during development while maintaining security for other connections.

Remember: These configurations are for development and testing only. Always use proper SSL certificates in production and remove these exceptions before shipping your app to users.


Questions or issues? Drop a comment below or check the official documentation links above.

Found this helpful? Share it with your fellow React Native developers! 🚀

Top comments (0)