php's implementation of curl
is very powerful. unfortunately, it's also frustratingly complex and the official docs can be terse and dense. every php developer who uses curl
frequently accumulates, over time, a directory of tried-and-true snippets for reference; a 'cheatsheet', basically. this is mine.
flyover
this article is going to cover (nearly) all of the basic use cases of php curl and provide examples that can be copy-pasted and modified. the topics are:
- an overview of a curl request
- a basic `GET` request
- reusing the pointer for multiple requests
- building and sending a query string
- getting the HTTP code with `curl_get_info`
- connecting to a different port
- error handling
- sending headers
- getting headers
- basic auth
- sending cookies
- getting cookies
- cookie jars
- sending
POST
requests - sending json via
POST
- sending form data via
POST
- sending
PUT
,PATCH
andDELETE
- downloading a file
- uploading a file
- using ftp
an overview of a curl request
a curl
script consists of four basic stages:
-
intialization: creating the curl handle using
curl_init()
-
setting options: using
curl_setopt()
to set all the options for the request; things like headers and body data and more. -
execution: running the curl request with
curl_exec()
and, optionally, getting the response data -
close: closing the handle we made with
curl_init()
there are, of course, other optional components that we may use. things like curl_getinfo()
to get data about a response or the CURLFile
object for crafting file uploads. but the core functionality of a curl request is built with of the four basic components.
a basic GET
request
running a basic http GET
request is fairly straightforward. we initialized curl using curl_init()
with the url as an argument and set the CURLOPT_RETURNTRANSFER
option to true so that we can get the response body.
$url = "http://example.ca/api/bands";
// initialize curl with the target url
$ch = curl_init($url);
// set CURLOPT_RETURNTRANSFER so we get a response body
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// run the curl request and get response body
$result = curl_exec($ch);
// close curl
curl_close($ch);
optionally, we can specify the url with the CURLOPT_URL
option rather than as an argument for curl_init()
.
$url = "http://example.ca/api/bands";
// initialize curl without an url
$ch = curl_init();
// set the url with CURLOPT_URL
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
reusing the curl pointer for multiple requests
we can reuse a curl handle to make multiple requests by changing the url with the CURLOPT_URL
option and rerunning curl_excec()
. here, we create one handle, then make two different GET
requests to two different urls.
$url_bands = "http://example.ca/api/bands";
$url_albums = "http://example.ca/api/albums";
// initialize curl without an url
$ch = curl_init();
// run first request
curl_setopt($ch, CURLOPT_URL, $url_bands);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
// run second request
curl_setopt($ch, CURLOPT_URL, $url_albums);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
building and sending a query string
sending a query string is straightforward: we append it to the url of the request.
// url with query string
$url = "http://example.ca/api/bands?name=bratmobile";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
query strings are annoying to build manually. fortunately, php provides the http_build_query
function to turn arrays into query strings.
$args = [
'artist' => 'monk, thelonious',
'years' => [
1957,
1963,
],
];
// convert array to query string
$query_string = http_build_query($args);
// append to url
$url = "http://example.ca/api/bands?".$query_string;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
getting HTTP code with curl_getinfo
the http response code can be extracted from the curl handle with curl_getinfo()
like this:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 200
curl_getinfo()
can also provide other useful information. popular data includes:
curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_getinfo($ch, CURLINFO_HTTP_VERSION);
curl_getinfo($ch, CURLINFO_PRIMARY_IP);
curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD);
connecting to a different port
the port can be set with the CURLOPT_PORT
option:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// option to set the port
curl_setopt($ch, CURLOPT_PORT, 8080);
$result = curl_exec($ch);
curl_close($ch);
error handling
curl can throw errors; it's a good idea to test for and handle them.
we do this by calling curl_errno()
to get the error number raised by curl_exec()
. if the result of curl_errno()
is 0, no error has ocurred.
if we have an error number greater than zero, we can get the human-readable error message with curl_error()
:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
// test for error
if(curl_errno($ch)) {
$error_message = curl_error($ch);
// handle errors
}
curl_close($ch);
there are a lot of potential curl errors. popular ones are:
CURLE_UNSUPPORTED_PROTOCOL (1)
CURLE_URL_MALFORMAT (3)
CURLE_COULDNT_RESOLVE_HOST (6)
CURLE_COULDNT_CONNECT (7)
CURLE_TOO_MANY_REDIRECTS (47)
treating http errors as curl errors
by default, http errors are not treated as errors by curl. we can change that by setting the CURLOPT_FAILONERROR
to true
. this will cause curl_errno
to return 22 (CURLE_HTTP_RETURNED_ERROR
) if the http server returns a code of 400 or greater.
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// treat any HTTP code >=400 as a curl error
curl_setopt($ch, CURLOPT_FAILONERROR, true);
$result = curl_exec($ch);
// test for error
if(curl_errno($ch)) {
$error_message = curl_error($ch);
// handle errors
}
curl_close($ch);
sending headers
headers can be set with the CURLOPT_HEADER
option. the option value passed here is an array of headers as strings:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// set the request headers
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-My-Special-Header: foo',
]);
$result = curl_exec($ch);
curl_close($ch);
getting headers
response headers and body are returned all in one string. we can extract a string of just the headers using substr
.
first, we need to explicitly tell curl to return the headers with the option CURLOPT_HEADER
.
extracting headers requires us to find the length in bytes of the header block with:
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
we can then use that length to write a substr
command to get a string from character 0 to the the header size. this is all the headers as a string.
to get the body content, we again use substr
to get a string from the end of the headers section to the end of the returned string.
it looks like this:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to get response body
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// option to get response headers
curl_setopt($ch, CURLOPT_HEADER, true);
$result = curl_exec($ch);
curl_close($ch);
// size of the headers in bytes
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
// string of all headers
$headersString = substr($result, 0, $headerSize);
// get body
$body = substr($result, $headerSize);
headers we get this way are a string, which is not particularly useful.
to turn that string of headers into an array of header values keyed by header names, we can do a little bit of work with explode
, array_map
and array_combine
:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$result = curl_exec($ch);
curl_close($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headersString = substr($result, 0, $headerSize);
// get array of all header lines
$headerLines = array_values(array_filter(explode(PHP_EOL, $headersString), fn($l) => preg_match('/^[A-Za-z0-9\-]+:/', $l)));
// get header names
$headerKeys = array_map(fn($h) => substr($h, 0, strpos($h, ":")), $headerLines);
// get header values
$headerValues = array_map(fn($h) => trim(substr($h, strpos($h, ":") + 1)), $headerLines);
// combine to array of [name] = value
$headersArray = array_combine($headerKeys, $headerValues);
functions to extract response headers
here is a convenience function for converting header strings returned by curl into nice, usable arrays:
/**
* Extract response headers from curl result as array keyed by header name
*
* @param string $result Return from curl_exec()
* @param CurlHandle $ch Return from curl_init()
* @return array<string,string>
*/
function getCurlResponseHeaders(string $result, CurlHandle $ch): array {
$headersString = substr($result, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$headersLines = array_values(array_filter(explode(PHP_EOL, $headersString), fn($l) => preg_match('/^[A-Za-z0-9\-]+:/', $l)));
return array_combine(array_map(fn($h) => substr($h, 0, strpos($h, ":")), $headersLines),
array_map(fn($h) => trim(substr($h, strpos($h, ":") + 1)), $headersLines));
}
sample output of this function will look something like this:
Array
(
[Server] => nginx/1.18.0 (Ubuntu)
[Content-Type] => application/json
[Transfer-Encoding] => chunked
[Connection] => keep-alive
[Set-Cookie] => woot=bar
[Cache-Control] => no-cache, private
[Date] => Wed, 20 Aug 2025 22:18:43 GMT
[Access-Control-Allow-Origin] => *
)
if you have two or more of the same header key, those elements will be overwritten with the last value.
this function sets header values as arrays, not strings, to allow more than one value per header key.
/**
* Extract response headers from curl as associated array keyed by header name of arrays of header values
*
* @param string $result The return from curl_exec
* @param CurlHandle $ch The return from curl_init
* @return array<string, <array string>>
*/
function getCurlResponseHeadersArrays(string $result, CurlHandle $ch): array {
$headersString = substr($result, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$headersLines = array_values(array_filter(explode(PHP_EOL, $headersString), fn($l) => preg_match('/^[A-Za-z0-9\-]+:/', $l)));
$headerKeys = array_unique(array_map(fn($h) => substr($h, 0, strpos($h, ":")), $headersLines));
$headersArray = [];
foreach($headersLines as $h) {
$headersArray[substr($h, 0, strpos($h, ":"))][] = trim(substr($h, strpos($h, ":") + 1));
}
return $headersArray;
}
output looks like this. notice that there are multiple values for the Set-Cookie
header:
Array
(
[Server] => Array
(
[0] => nginx/1.18.0 (Ubuntu)
)
[Content-Type] => Array
(
[0] => application/json
)
[Set-Cookie] => Array
(
[0] => favourite_year=1993
[1] => preferred_format=EP
)
)
both of these functions are available as gists.
basic auth
making requests to sites protected by basic auth can be done by setting the CURLOPT_HTTPAUTH
option to CURLAUTH_BASIC
and setting the CURLOPT_USERPWD
option with the username and password:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// set to use basic auth
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
// set basic auth username and password
curl_setopt($ch, CURLOPT_USERPWD, "<username>:<password>");
$result = curl_exec($ch);
curl_close($ch);
alternately, we can send an Authorization:
header with the username and password base64 encoded:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// set base64 of username and password in header
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Basic '. base64_encode("<username>:<password>")
]);
$result = curl_exec($ch);
curl_close($ch);
sending cookies
we can send cookies to a site as a string of name=value pairs using the CURLOPT_COOKIE
option.
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// set cookies as string
curl_setopt($ch, CURLOPT_COOKIE, "favourite_year=1993;preferred_format=EP");
$result = curl_exec($ch);
curl_close($ch);
or, if we prefer, we can send them using the Cookie
header:
$url = "http://example.ca/api/cookies";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// set cookies as string using headers
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Cookie: favourite_year=1993;preferred_format=EP',
]);
$result = curl_exec($ch);
curl_close($ch);
getting cookies
cookies set by a site are returned in the Set-Cookie
header. This requires us to extract them using, for instance, the getCurlResponseHeadersArrays
function shown in the section on getting headers above.
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to get response body
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// option to get response headers
curl_setopt($ch, CURLOPT_HEADER, true);
$result = curl_exec($ch);
curl_close($ch);
/**
* Extract response headers from curl as associated array of arrays containing header values
*
* @param string $result The return from curl_exec
* @param CurlHandle $ch The return from curl_init
* @return array<string, <array string>>
*/
function getCurlResponseHeadersArrays(string $result, CurlHandle $ch): array {
$headersString = substr($result, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
$headersLines = array_values(array_filter(explode(PHP_EOL, $headersString), fn($l) => preg_match('/^[A-Za-z0-9\-]+:/', $l)));
$headerKeys = array_unique(array_map(fn($h) => substr($h, 0, strpos($h, ":")), $headersLines));
$headersArray = [];
foreach($headersLines as $h) {
$headersArray[substr($h, 0, strpos($h, ":"))][] = trim(substr($h, strpos($h, ":") + 1));
}
return $headersArray;
}
// extract cookies from headers as array
$cookiesArray = getCurlResponseHeadersArrays($result, $ch)['Set-Cookie'];
the output of this example is two cookies:
Array
(
[0] => favourite_year=1993
[1] => preferred_format=EP
)
cookie jars
persisting cookies between requests is probably something we will need to do. this can be done by saving cookies to the filesystem in files known colloquially as 'cookie jars'
getting cookies into file
to save the cookies a site has sent to a file, we set the CURLOPT_COOKIEJAR
to the path of the file we wish to use:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to set file where downloaded cookies are stored
curl_setopt($ch, CURLOPT_COOKIEJAR, "/path/to/cookiejar");
$result = curl_exec($ch);
curl_close($ch);
if we look at the contents of a cookie jar, we can see the cookie data.
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
example.ca FALSE /api/ FALSE 0 preferred_format EP
example.ca FALSE /api/ FALSE 0 favourite_year 1993
like the comments say, don't edit this file.
sending cookies from a file
to send cookies from a file, we set the CURLOPT_COOKIEFILE
option to the path of the file we wish to use:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to set file where cookies to send are stored
curl_setopt($ch, CURLOPT_COOKIEFILE, "/path/to/cookiejar");
$result = curl_exec($ch);
curl_close($ch);
note: the file we send cookies from is defined by CURLOPT_COOKIEFILE
. the file we receive cookies into is CURLOPT_COOKIEJAR
.
sending and getting cookies using a file
we can send and receive cookies using files in the same request using the same file. this is a good strategy for constantly keeping cookies fresh.
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to set file where cookies to send are stored
curl_setopt($ch, CURLOPT_COOKIEFILE, "/path/to/cookiejar");
// option to set file where downloaded cookies are stored
curl_setopt($ch, CURLOPT_COOKIEJAR, "/path/to/cookiejar");
$result = curl_exec($ch);
curl_close($ch);
sending POST
requests
we can explicitly set the http method to POST
in two ways: using the CURLOPT_POST
or CURLOPT_CUSTOMREQUEST
options.
to use CURLOPT_POST
:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to set http method as POST
curl_setopt($ch, CURLOPT_POST, true);
$result = curl_exec($ch);
curl_close($ch);
important: using the option CURLOPT_POST
automatically sends the header Content-Type: application/x-www-form-urlencoded
if we want to avoid sending that header we can use the CURLOPT_CUSTOMREQUEST
option like so:
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to set http method as POST
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
$result = curl_exec($ch);
curl_close($ch);
sending json via POST
sending json data via post is done by setting the CURLOPT_POSTFIELDS
option with the json data and setting the Content-Type: application/json
header:
$url = "http://example.ca/api/bands";
// build json for request body
$data = ['name' => 'bratmobile'];
$json = json_encode($data);
$ch = curl_init($url);
// option to set body of request
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
// header to set request content type as json
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
]);
$result = curl_exec($ch);
curl_close($ch);
setting CURLOPT_POSTFIELDS
automatically sets the http method to POST
.
sending form data via POST
form data can be sent via curl by setting the option CURLOPT_POSTFIELDS
with the form data. form data is formatted like a query string. we can use http_build_query
here to help.
$args = [
'artist' => 'monk, thelonious',
'years' => [
1957,
1963
]
];
// convert array to form data
$postFields = http_build_query($args);
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// option to set post fields
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
$result = curl_exec($ch);
curl_close($ch);
setting the CURLOPT_POSTFIELDS
option automatically sets the method to POST
and adds the request header Content-Type: application/x-www-form-urlencoded
sending PUT
, PATCH
and DELETE
the PUT
, PATCH
and DELETE
methods can be sent using the CURLOPT_CUSTOMREQUEST
option
$url = "http://example.ca/api/bands";
$ch = curl_init($url);
// option to set http method as PUT
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); // or PATCH or DELETE
$result = curl_exec($ch);
curl_close($ch);
downloading a file
downloading a file requires a file write handler created by fopen()
to be passed to the option CURLOPT_FILE
:
$url = "http://example.ca/api/bands/bratmobile.jpg";
// file pointer for downloaded file.
$outputFp = fopen("/path/to/image.jpg", "wb");
$ch = curl_init($url);
// option to set file pointer where downloaded file will be saved
curl_setopt($ch, CURLOPT_FILE, $outputFp);
$result = curl_exec($ch);
curl_close($ch);
uploading a file
uploading a file requires creating a CURLFile
object with the path and mimetype of the file to upload and then assigning that object to the CURLOPT_POSTFIELDS
option.
note that postname
here is the name of the file as the receiving server sees it.
$url = "http://example.ca/api/bands";
// path of file to upload
$path = "/path/to/file.txt";
// get mime type of file from content
$mime = mime_content_type($path);
// the key of the file. usually 'file'
$postname = 'file';
// create CURLFile to upload in post fields
$curlFile = new CURLFile($path, $mime, $postname);
$ch = curl_init($url);
// option to set CURLFile in post fields
curl_setopt($ch, CURLOPT_POSTFIELDS, [$curlFile->postname => $curlFile]);
$result = curl_exec($ch);
curl_close($ch);
using ftp
php has an existing suite of ftp commands that are almost certainly more useful than curl. consider using those instead.
running ftp commands
we can log into an ftp server by setting the CURLOPT_USERPWD
option to the username and password of the account separated with a colon.
sending commands to the ftp server is done with the CURLOPT_CUSTOMREQUEST
option.
multiple ftp commands can be run per connection.
$url = "ftp://ftp.example.ca";
// ftp username and password
$username = "someftpuser";
$password = "secretpassword";
$ch = curl_init($url);
// set username and password
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// optionally set verbose output for debugging
// curl_setopt($ch, CURLOPT_VERBOSE, true);
// execute HELP command
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HELP');
$result = curl_exec($ch);
// execute CWD command to cd to a directory
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'CWD /path/to/dir');
$result = curl_exec($ch);
// execute LIST command to ls the current directory
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'LIST');
$directoryList = curl_exec($ch);
// output results of LIST
print_r($directoryList);
curl_close($ch);
important note: sending the HELP
command to the ftp server will return a list of valid commands. only those commands will be accepted by the ftp server.
an example of the output from the HELP
command is:
214-The following commands are recognized.
ABOR ACCT ALLO APPE CDUP CWD DELE EPRT EPSV FEAT HELP LIST MDTM MKD
MODE NLST NOOP OPTS PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR
RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
XPWD XRMD
note: setting the CURLOPT_VERBOSE
option to true
will output all ftp traffic and is useful in inspecting/debugging. some commands, such as HELP
may not return output and can only be read when the verbose option is turned on.
uploading a file via ftp
uploading a file via ftp requires several steps:
- set the
CURLOPT_UPLOAD
option totrue
- set the
CURLOPT_INFILE
option to a file pointer of the file created byfopen
- set the
CURLOPT_INFILESIZE
option to the size of the file fromfilesize()
- set the url of the request to the path and name of the uploaded file
// full path to file to upload
$file = "/tmp/filetoupload";
// open file and get pointer
$fp = fopen($file, "r");
// build url including path of target file
$url = "ftp://ftp.example.ca/path/to/dir/".basename($file);
// ftp username and password
$username = "someftpuser";
$password = "secretpassword";
$ch = curl_init($url);
// set username and password
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// option to set upload
curl_setopt($ch, CURLOPT_UPLOAD, true);
// option to set pointer to file to upload
curl_setopt($ch, CURLOPT_INFILE, $fp);
// option to set size of file
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($file));
$result = curl_exec($ch);
curl_close($ch);
downloading a file via ftp
downloading a file from an ftp server involves making an ftp request to the file and then writing the result of curl_exec
to a file.
// path to remote file on server
$remoteFile = "/TEST_OUT/sampleout";
// path to file where the downloaded file will be saved
$localFile = "/tmp/downloadedfile";
// open download file and get pointer
$fp = fopen($localFile, "w");
// url of ftp server including path of file to download
$url = "ftp://ftp.avpbooks.com".$remoteFile;
// ftp username and password
$username = "avpftpuser";
$password = "RadialContentsGopherKelp98!3";
$ch = curl_init($url);
// set username and password
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
// write result to file
fwrite($fp, $result);
curl_close($ch);
Top comments (0)