Skip to content

Flutter HTTP Request SSL Error Fix

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about Flutter HTTP Request SSL Error Fix. We cover key concepts, practical examples, and best practices.

Your Flutter app fails to make HTTP requests with HandshakeException: Handshake error, CERTIFICATE_VERIFY_FAILED, or SocketException — the SSL/TLS certificate cannot be verified or the server uses an unsupported TLS version.

Step-by-Step Fix

1. Use HttpClient with proper certificate handling

import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;

// Wrong: ignoring SSL errors (insecure, development only!)
class InsecureHttpClient {
  static final http.Client client = http.Client();
}

// Right: use proper HttpClient with timeouts
class ApiService {
  static final http.Client client = http.Client();

  Future<Map<String, dynamic>> fetchData(String url) async {
    try {
      final response = await client
          .get(Uri.parse(url))
          .timeout(const Duration(seconds: 10));

      if (response.statusCode == 200) {
        return json.decode(response.body) as Map<String, dynamic>;
      } else {
        throw HttpException('Status: ${response.statusCode}');
      }
    } on SocketException {
      throw Exception('No internet connection');
    } on HttpException {
      throw Exception('Server error');
    } on FormatException {
      throw Exception('Invalid response format');
    }
  }
}

2. Configure iOS App Transport Security

<!-- ios/Runner/Info.plist -->
<!-- Wrong: ATS blocks all HTTP connections -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
</dict>

<!-- Right: allow specific domains with HTTPS -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/>
    <key>NSExceptionDomains</key>
    <dict>
        <key>api.myapp.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <false/>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSRequiresCertificateTransparency</key>
            <true/>
        </dict>
    </dict>
</dict>

3. Configure Android network security

<!-- android/app/src/main/res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- Wrong: allowing cleartext for all domains -->
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <!-- Right: specific domain configuration -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.myapp.com</domain>
        <domain includeSubdomains="true">cdn.myapp.com</domain>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </domain-config>

    <!-- Debug-only: allow cleartext for local development -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>
<!-- android/app/src/main/AndroidManifest.xml -->
<application
    android:networkSecurityConfig="@xml/network_security_config"
    ...>

4. Use badCertificateCallback for self-signed certs (development)

import 'dart:io';
import 'package:http/http.dart' as http;

// For DEVELOPMENT ONLY: handle self-signed certificates
class DevHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) {
        // In development, accept specific host certificates
        return host == 'localhost' || host == '192.168.1.100';
      };
  }
}

// Register in main()
void main() {
  HttpOverrides.global = DevHttpOverrides();
  runApp(MyApp());
}

// For production: never bypass certificate validation

5. Handle TLS version compatibility

import 'dart:io';

class SecureHttpClient {
  static HttpClient create() {
    final client = HttpClient();
    // Require minimum TLS 1.2
    client.badCertificateCallback = null; // No bypass
    return client;
  }

  static Future<String> get(String url) async {
    final client = create();
    try {
      final request = await client.getUrl(Uri.parse(url));
      final response = await request.close();
      return await response.transform(utf8.decoder).join();
    } finally {
      client.close();
    }
  }
}

Prevention

  • Always use HTTPS URLs in production Flutter apps.
  • Configure iOS ATS and Android network security properly for production.
  • Never use badCertificateCallback returning true in production.
  • Use the http package with timeouts to handle network failures gracefully.
  • Keep the root CA certificates updated on the device.

Common Mistakes with http request

  1. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  2. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  3. Using return to exit a function early instead of wrapping a pure value in the monad

These mistakes appear frequently in real-world FLUTTER code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

Why does my Flutter HTTPS request fail on Android but work on iOS?

Android uses a different trust store. If the server uses an SSL certificate from a CA not trusted by Android (e.g., some self-signed or Let's Encrypt old chains), the connection fails. Update the device's CA certificates. |||What is "CERTIFICATE_VERIFY_FAILED" error? The server's SSL certificate cannot be verified against the device's trusted root CA store. This happens with self-signed certificates, expired certificates, or when the device's clock is incorrect. |||Can I bypass SSL verification in production? No, never bypass SSL verification in production builds. This creates a man-in-the-middle vulnerability. Only use badCertificateCallback during development with local servers.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro