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)