Flutter HTTP Request SSL Error Fix
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
badCertificateCallbackreturningtruein production. - Use the
httppackage with timeouts to handle network failures gracefully. - Keep the root CA certificates updated on the device.
Common Mistakes with http request
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists - Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
- Using
returnto 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
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro