[php] Can't connect to HTTPS site using cURL. Returns 0 length content instead. What can I do?

I have a site that connects using cURL (latest version) to a secure gateway for payment.

The problem is cURL always returns 0 length content. I get headers only. And only when I set cURL to return headers. I have the following flags in place.

curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_URL, $gatewayURI);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_POST, 1);

The header returned is

HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Date: Tue, 25 Nov 2008 01:08:34 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Content-Length: 0
Content-Type: text/html
Set-Cookie: ASPSESSIONIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; path=/
Cache-control: private

I have also tried cURL'ing different sites and they return content fine. I think the problem might have something to do with the https connection.

I have spoken with the company and they are unhelpful.

Has anyone else experienced this error and know a work around? Should I ditch cURL and try and use fsockopen() ?

Thank you. :)

This question is related to php curl https

The answer is


there might be a problem at your web hosting company from where you are testing the secure communication for gateway, that they might not allow you to do that.

also there might be a username, password that must be provided before connecting to remote host.

or your IP might need to be in the list of approved IP for the remote server for communication to initiate.


Note: This is strictly not production use. If you want to quickly debug, this may be useful. Otherwise, please use @SchizoDuckie's answer above.

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);     
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); 

Just add them. It works.


I had the same problem today. Curl comes with an outdated file to authenticate HTTPS certificates from.

get the new one from:

http://curl.haxx.se/ca/cacert.pem

save it into some dir on your site

and add

curl_setopt ($curl_ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem"); 

To every request :-)

IGNORE any dumbass comments about disabling CURLOPT_VERIFYPEER and CURLOPT_VERIFYHOST!! That leaves your code vulnerable to man in the middle attacks!

December 2016 edit:

Solve this properly by using Jasen's method mentioned below.

add curl.cainfo=/etc/ssl/certs/ca-certificates.crt to you php.ini

October 2017 edit:

There is now a composer package that helps you manage the ca certificates, so that you're not vulnerable if your cacert.pem becomes outdated due to revoking certificates.

https://github.com/paragonie/certainty -> composer require paragonie/certainty:dev-master


Just had a very similar problem and solved it by adding

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

Apparently the site I'm fetching redirects to another location and php-curl doesn't follow redirects by default.


there might be a problem at your web hosting company from where you are testing the secure communication for gateway, that they might not allow you to do that.

also there might be a username, password that must be provided before connecting to remote host.

or your IP might need to be in the list of approved IP for the remote server for communication to initiate.


Sometimes you have to upgrade your Curl certificates to latest version not to have errors with https.


You are using POST method, but are you providing an array of data? E.g.

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

Just had a very similar problem and solved it by adding

curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

Apparently the site I'm fetching redirects to another location and php-curl doesn't follow redirects by default.


Sometimes you have to upgrade your Curl certificates to latest version not to have errors with https.


Whenever I'm testing something with PHP/Curl, I try it from the command line first, figure out what works, and then port my options to PHP.


I used file_get_contents using stream_create_context and it works fine:

$postdataStr = http_build_query($postdataArr);

$context_options = array (
        'http' => array (    <blink> // this will allways be http!!!</blink>
            'method' => 'POST',
            'header'=> "Content-type: application/x-www-form-urlencoded\r\n"
                . "Content-Length: " . strlen($postdataArr) . "\r\n"
                . "Cookie: " . $cookies."\r\n"
            'content' => $postdataStr
            )
        );

$context = stream_context_create($context_options);
$HTTPSReq = file_get_contents('https://www.example.com/', false, $context);

I had the same problem today. Curl comes with an outdated file to authenticate HTTPS certificates from.

get the new one from:

http://curl.haxx.se/ca/cacert.pem

save it into some dir on your site

and add

curl_setopt ($curl_ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem"); 

To every request :-)

IGNORE any dumbass comments about disabling CURLOPT_VERIFYPEER and CURLOPT_VERIFYHOST!! That leaves your code vulnerable to man in the middle attacks!

December 2016 edit:

Solve this properly by using Jasen's method mentioned below.

add curl.cainfo=/etc/ssl/certs/ca-certificates.crt to you php.ini

October 2017 edit:

There is now a composer package that helps you manage the ca certificates, so that you're not vulnerable if your cacert.pem becomes outdated due to revoking certificates.

https://github.com/paragonie/certainty -> composer require paragonie/certainty:dev-master


I discovered this error on a recent application project. I was writing to run from the command line or the browser window, so I was using server detection to get the relative URL of the document I was asking for. The trouble was, the site is https, and each time I attempted to access http://(same server), cURL helpfully changed it to https.

This works fine from the browser, but from the command-line, I'd then get an SSL error even with both verify's set to false. What I had to do was,

1) Check $_SERVER['HTTP_HOST']. If present, use ($_SERVER['HTTPS'] ? "https://" : "http://").$_SERVER['HTTP_HOST']

2) Check $_SERVER['COMPUTERNAME'], and if it matched the production server, provide the https URL. ("https://(servername)")

3) If neither condition passed, it means I'm running command-line on a different server, and use "http://localhost".

Now, this worked, but it's a hack. Also, I never did figure out why on one server (https) cURL changed my URL, while on the other (also https) it left my URL alone.

Weird.


I used file_get_contents using stream_create_context and it works fine:

$postdataStr = http_build_query($postdataArr);

$context_options = array (
        'http' => array (    <blink> // this will allways be http!!!</blink>
            'method' => 'POST',
            'header'=> "Content-type: application/x-www-form-urlencoded\r\n"
                . "Content-Length: " . strlen($postdataArr) . "\r\n"
                . "Cookie: " . $cookies."\r\n"
            'content' => $postdataStr
            )
        );

$context = stream_context_create($context_options);
$HTTPSReq = file_get_contents('https://www.example.com/', false, $context);

Whenever I'm testing something with PHP/Curl, I try it from the command line first, figure out what works, and then port my options to PHP.


I had a situation where this helped: (PHP 5.4.16 on Windows)

curl_setopt($ch, CURLOPT_SSLVERSION, 3);

You are using POST method, but are you providing an array of data? E.g.

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

Note: This is strictly not production use. If you want to quickly debug, this may be useful. Otherwise, please use @SchizoDuckie's answer above.

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);     
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); 

Just add them. It works.


The best way to use https and avoid security issues is to use Firefox (or another tool) and download the certificate to your server. This webpage helped me a lot, and these were the steps that worked for me:

1) Open in Firefox the URL you're gonna use with CURL

2) On the address bar click on the padlock > more information (FF versions can have different menus, just find it). Click the View certificate button > Details tab.

3) Highlight the "right" certificate in Certificate hierarchy. In my case it was the second of three, called "cPanel, Inc. Certification Authority". I just discovered the right one by "trial and error" method.

4) Click the Export button. In my case the one who worked was the file type "PEM with chains" (again by trial and error method).

5) Then in your PHP script add:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);    
curl_setopt($ch, CURLOPT_CAINFO, [PATH_TO_CRT_FILE]);

In addition I'd say that we must pay attention on the fact that these steps will probably need to be redone once a year or whenever the URL certificate is replaced or renewed.


I discovered this error on a recent application project. I was writing to run from the command line or the browser window, so I was using server detection to get the relative URL of the document I was asking for. The trouble was, the site is https, and each time I attempted to access http://(same server), cURL helpfully changed it to https.

This works fine from the browser, but from the command-line, I'd then get an SSL error even with both verify's set to false. What I had to do was,

1) Check $_SERVER['HTTP_HOST']. If present, use ($_SERVER['HTTPS'] ? "https://" : "http://").$_SERVER['HTTP_HOST']

2) Check $_SERVER['COMPUTERNAME'], and if it matched the production server, provide the https URL. ("https://(servername)")

3) If neither condition passed, it means I'm running command-line on a different server, and use "http://localhost".

Now, this worked, but it's a hack. Also, I never did figure out why on one server (https) cURL changed my URL, while on the other (also https) it left my URL alone.

Weird.


The best way to use https and avoid security issues is to use Firefox (or another tool) and download the certificate to your server. This webpage helped me a lot, and these were the steps that worked for me:

1) Open in Firefox the URL you're gonna use with CURL

2) On the address bar click on the padlock > more information (FF versions can have different menus, just find it). Click the View certificate button > Details tab.

3) Highlight the "right" certificate in Certificate hierarchy. In my case it was the second of three, called "cPanel, Inc. Certification Authority". I just discovered the right one by "trial and error" method.

4) Click the Export button. In my case the one who worked was the file type "PEM with chains" (again by trial and error method).

5) Then in your PHP script add:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);    
curl_setopt($ch, CURLOPT_CAINFO, [PATH_TO_CRT_FILE]);

In addition I'd say that we must pay attention on the fact that these steps will probably need to be redone once a year or whenever the URL certificate is replaced or renewed.


I had the same problem today. Curl comes with an outdated file to authenticate HTTPS certificates from.

get the new one from:

http://curl.haxx.se/ca/cacert.pem

save it into some dir on your site

and add

curl_setopt ($curl_ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem"); 

To every request :-)

IGNORE any dumbass comments about disabling CURLOPT_VERIFYPEER and CURLOPT_VERIFYHOST!! That leaves your code vulnerable to man in the middle attacks!

December 2016 edit:

Solve this properly by using Jasen's method mentioned below.

add curl.cainfo=/etc/ssl/certs/ca-certificates.crt to you php.ini

October 2017 edit:

There is now a composer package that helps you manage the ca certificates, so that you're not vulnerable if your cacert.pem becomes outdated due to revoking certificates.

https://github.com/paragonie/certainty -> composer require paragonie/certainty:dev-master


Whenever I'm testing something with PHP/Curl, I try it from the command line first, figure out what works, and then port my options to PHP.


You are using POST method, but are you providing an array of data? E.g.

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

Whenever I'm testing something with PHP/Curl, I try it from the command line first, figure out what works, and then port my options to PHP.


I had the same problem today. Curl comes with an outdated file to authenticate HTTPS certificates from.

get the new one from:

http://curl.haxx.se/ca/cacert.pem

save it into some dir on your site

and add

curl_setopt ($curl_ch, CURLOPT_CAINFO, dirname(__FILE__)."/cacert.pem"); 

To every request :-)

IGNORE any dumbass comments about disabling CURLOPT_VERIFYPEER and CURLOPT_VERIFYHOST!! That leaves your code vulnerable to man in the middle attacks!

December 2016 edit:

Solve this properly by using Jasen's method mentioned below.

add curl.cainfo=/etc/ssl/certs/ca-certificates.crt to you php.ini

October 2017 edit:

There is now a composer package that helps you manage the ca certificates, so that you're not vulnerable if your cacert.pem becomes outdated due to revoking certificates.

https://github.com/paragonie/certainty -> composer require paragonie/certainty:dev-master


there might be a problem at your web hosting company from where you are testing the secure communication for gateway, that they might not allow you to do that.

also there might be a username, password that must be provided before connecting to remote host.

or your IP might need to be in the list of approved IP for the remote server for communication to initiate.


Examples related to php

I am receiving warning in Facebook Application using PHP SDK Pass PDO prepared statement to variables Parse error: syntax error, unexpected [ Preg_match backtrack error Removing "http://" from a string How do I hide the PHP explode delimiter from submitted form results? Problems with installation of Google App Engine SDK for php in OS X Laravel 4 with Sentry 2 add user to a group on Registration php & mysql query not echoing in html with tags? How do I show a message in the foreach loop?

Examples related to curl

What is the incentive for curl to release the library for free? curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number Converting a POSTMAN request to Curl git clone error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054 How to post raw body data with curl? Curl : connection refused How to use the curl command in PowerShell? Curl to return http status code along with the response How to install php-curl in Ubuntu 16.04 curl: (35) SSL connect error

Examples related to https

What's the net::ERR_HTTP2_PROTOCOL_ERROR about? Requests (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.") Error in PyCharm requesting website Android 8: Cleartext HTTP traffic not permitted ssl.SSLError: tlsv1 alert protocol version Invalid self signed SSL cert - "Subject Alternative Name Missing" How do I make a https post in Node Js without any third party module? Page loaded over HTTPS but requested an insecure XMLHttpRequest endpoint How to force Laravel Project to use HTTPS for all routes? Could not create SSL/TLS secure channel, despite setting ServerCertificateValidationCallback Use .htaccess to redirect HTTP to HTTPs