When accessing an external API or HTTPS endpoint, it is not uncommon to encounter the following PHP cURL error:
Recv failure: Connection was aborted
This issue is especially confusing when the same URL works perfectly from:
- a web browser
- the command line (
curl) - tools like Postman
…but fails only from PHP code.
This article explains why this happens, how to diagnose the real cause, and provides battle-tested solutions applicable to enterprise and production environments.
What the Error Actually Means
The error:
Recv failure: Connection was aborted
means that:
- A TCP connection was successfully established
- The remote server closed the connection abruptly
- PHP cURL did not receive a valid HTTP response
This is not a DNS error, nor a simple timeout. It is typically caused by server-side security policies, TLS negotiation issues, or request fingerprinting differences.
Why It Works in Browser or CLI but Not in PHP
This discrepancy is the key diagnostic clue.
Browsers and CLI curl:
- Send User-Agent headers
- Support modern TLS ciphers
- Automatically negotiate HTTP/2 / ALPN
- May pass through different proxies or networks
PHP cURL:
- Uses libcurl + OpenSSL bundled with PHP
- Often sends no User-Agent
- May use older TLS versions
- Runs under restricted firewall / SELinux / hosting rules
Many APIs silently drop connections that do not meet their security expectations.
Most Common Root Causes
1. Missing or Rejected User-Agent (Very Common)
Some API gateways explicitly reject requests without a User-Agent.
Symptom
- Connection aborted immediately
- No HTTP status code
Fix
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0');
2. TLS / SSL Protocol Mismatch
PHP might be using:
- TLS 1.0 / 1.1
- Old OpenSSL
- Unsupported cipher suites
The server closes the connection during the TLS handshake.
Fix
Force modern TLS:
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
Check OpenSSL version:
php -i | grep OpenSSL
3. Server Blocks “Bot-like” Requests
Many gateways (API Gateway, WAF, Cloudflare, Nginx security rules) block:
- Requests without headers
- Requests without Accept
- Requests that look automated
Fix
Send minimal browser-like headers:
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Accept: application/json',
'Connection: keep-alive'
]);
4. HTTP/2 or ALPN Issues
Some PHP builds fail during HTTP/2 negotiation.
Fix
Force HTTP/1.1:
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
5. Proxy / Firewall / Hosting Restrictions
Common in:
- Shared hosting
- Corporate networks
- Hardened Linux servers
The outbound connection is aborted mid-stream.
Checks
- Compare server IP vs local machine IP
- Test from the same server using CLI curl
- Check firewall rules (iptables, firewalld)
6. Incorrect cURL Execution Order (Logic Bug)
A subtle but critical mistake:
curl_getinfo($ch); // ❌ before curl_exec()
curl_exec() must be called first, otherwise no request is executed.
Correct, Production-Ready PHP cURL Example
<?php
$url = 'https://example.com/api/v2/resource/123';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
// Security / compatibility
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
// Headers
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP-cURL)',
CURLOPT_HTTPHEADER => [
'Accept: application/json'
],
]);
$response = curl_exec($ch);
if ($response === false) {
echo 'cURL error: ' . curl_error($ch);
} else {
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP status: $status\n";
echo $response;
}
curl_close($ch);
Advanced Debugging Techniques
Enable Verbose Output
curl_setopt($ch, CURLOPT_VERBOSE, true);
This reveals:
- TLS handshake steps
- Cipher negotiation
- Exact moment the connection is aborted
Compare With CLI curl
Run on the same server:
curl -v https://example.com/api/v2/resource/123
Differences in:
- TLS version
- Headers
- IP routing
…usually reveal the root cause.
When Disabling SSL Verification Does NOT Help
Setting:
CURLOPT_SSL_VERIFYPEER => false
does not fix:
- TLS protocol mismatch
- Cipher incompatibility
- WAF blocking
- Missing headers
It only bypasses certificate validation, not connection policies.
Best Practices for Stable API Calls in PHP
- Always set a User-Agent
- Force TLS 1.2+
- Use HTTP/1.1 if HTTP/2 causes issues
- Capture and log
curl_error()andcurl_getinfo() - Keep OpenSSL and PHP updated
- Test from the same host as PHP runs
Summary
The error “Recv failure: Connection was aborted” is rarely a PHP bug. It is almost always caused by server security rules, TLS negotiation, or request fingerprinting.
By:
- Sending proper headers
- Forcing modern TLS
- Executing cURL correctly
- Matching browser behavior
…you can make PHP cURL requests as reliable as browser or CLI calls.


