PHP and cURL: How WordPress Makes HTTP Requests

#
By Mike Davey, Senior Editor

cURL is the workhorse of the modern internet. As its tagline says, cURL is a utility piece of software used to ‘transfer data with urls‘. According to the cURL website, the library is used by billions of people daily in everything from cars to mobile phones. It’s the networking backbone of thousands of applications and services..

Many PHP projects and libraries that need to send or receive data over the network use cURL as the default network library under the hood. Unsurprisingly, it’s also a core utility used by the WordPress Requests API as well as our plugins WP Migrate, WP Offload Media, and WP Offload SES.

If you’re curious about the power of the cURL library and how it works with WordPress, then you’re in the right place.

What is cURL?

Let’s start by going over what cURL is. cURL is really two pieces, libcurl which is the C library that does all the magic, and the cURL CLI program. Programming languages like PHP include the libcurl library as a module, allowing them to provide the cURL functionality natively.

The libcurl library is an open source URL transfer library and supports a wide variety of protocols. Not just HTTP, but HTTPS, SCP, SFTP, HTTP/2, HTTP/3, and even Gopher. Pretty much every protocol you can imagine – cURL supports.

cURL has been around since 1998 and has been actively maintained and updated since then. As of 2024, cURL is still a powerful and modern tool, with ongoing development and support. While it does have its quirks and issues, it’s essential for developers to know how it works and what it does.

cURL is kind of like an Internet Swiss Army knife. Essentially, if you have a piece of software that needs to make a network request–be it an HTTP POST request to a remote URL or an SFTP file download–cURL is often the most straightforward choice.

For example, to send a HTTP POST request with a file upload, using the cURL CLI, run:

curl --form name=Peter --form age=34 --form upload=@/Users/petertasker/photos/image-1.jpg http://httpbin.org/post

How about downloading a large file?

curl -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz

Or, getting the HTTP headers from a server?

curl -I https://deliciousbrains.com

Which will return the HTTP headers:

HTTP/2 200 
date: Fri, 01 Nov 2024 19:42:10 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
vary: Accept-Encoding
vary: Accept-Encoding
vary: Accept-Encoding,Cookie
x-powered-by: WP Engine
link: <https://deliciousbrains.com/wp-json/>; rel="https://api.w.org/"
link: <https://deliciousbrains.com/wp-json/wp/v2/pages/28>; rel="alternate"; title="JSON"; type="application/json"
link: <https://deliciousbrains.com/>; rel=shortlink
x-cacheable: SHORT
cache-control: max-age=600, must-revalidate
x-cache: HIT: 21
x-cache-group: normal
strict-transport-security: max-age=63072000

cURL is available on pretty much every platform and can be installed quickly and easily. If you’ve got a web server, chances are you’ve got cURL.

For example, if you don’t have cURL already installed on macOS, you can quickly get it with Homebrew by running:

brew install curl

As of macOS 12.3, cURL is included by default, so you may not need to install it separately. It’s also included in Windows.

So far we’ve looked at cURL as a CLI tool, but cURL bindings are also available for most languages, including PHP. If you’re using PHP software that makes network requests, you’ve likely been using cURL!

cURL and PHP

In PHP, cURL support is like any other module that you may rely on, like mysqli or the GD image library.

Most versions of PHP are compiled with cURL by default, but cURL integration is technically an extension, just like mysqli and everything else listed in the extensions section of your phpinfo() output.

Whereas the cURL CLI tool is relatively straightforward, PHP’s implementation is a bit more complicated.

When working with PHP’s implementation of cURL you’re required to use the curl_setopt() function. This function lets you set cURL options. For example, setting up an HTTP POST request looks like this:

$curl = curl_init( 'https://httpbin.org/post' );
curl_setopt( $curl, CURLOPT_POST, true );
curl_setopt( $curl, CURLOPT_POSTFIELDS, array( 'field1' => 'some data', 'field2' => 'some more data' ) );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
$response = curl_exec( $curl );
curl_close( $curl );

Making requests this way can get a little out of hand with larger requests and more complicated CURLOPT_ parameters.

Fortunately, the wonderful PHP community has created libraries that abstract a lot of the complexity away. Two of the more popular networking libraries are Guzzle and Requests. Because Requests supports older versions of PHP and WordPress historically supported really old versions of PHP (😩), the Requests library is used in WordPress core.

As of PHP 8.0, the curl extension is enabled by default, and the curl version is typically the latest stable version available. You can check the curl version using the phpinfo() function or by running php -i | grep "cURL" in your terminal.

cURL in WordPress

Internally, WordPress uses the WP_Http class for network requests, which in turn relies on the Requests library. This means that all of the HTTP utility methods like wp_remote_get() and wp_remote_post() use Requests. At a high level, WordPress updates, plugin downloads, plugin updates, and pretty much any upload/download functionality in WordPress core are using the Requests abstraction of the cURL bindings and options.

Let’s take a quick peek at how the Requests library makes HTTP requests. If you pop open wp-includes/class-wp-http.php, you’ll be able to check out a lot of the internal plumbing that drives HTTP requests within WordPress.

In WordPress 6.6, you can find this call on line 397 of the WP_Http class referenced above.

$requests_response = WpOrg\Requests\Requests::request( $url, $headers, $data, $type, $options );

Pretty simple right? Now compare the HTTP POST request below to the raw CURLOPTS method above:

$data = array( 'key1' => 'value1', 'key2' => 'value2' );
$response = Requests::post( 'http://httpbin.org/post', array(), $data );

Much simpler. And if you’re working in the context of a WordPress plugin or theme, you can use the wp_remote_post() function for even more abstraction:

$data = array( 'key1' => 'value1', 'key2' => 'value2' );
$response = wp_remote_post( 'http://httpbin.org/post', array( 'data' => $data ) );

How do I update cURL in WordPress?

Well, you don’t. WordPress doesn’t have its own cURL library but relies on the version of cURL that the PHP installation provides. The PHP cURL extension is a wrapper around libcurl and will use the version of libcurl that’s installed on the server.

So upgrading cURL for WordPress will almost always be the same as upgrading cURL on the web server. Unless you manage it yourself, this is something you need to ask the server administrator or hosting company to help you with.

The most obvious sign that the version of cURL on your server is too old is that WordPress can’t connect to remote servers via HTTPS. The error message to look for is cURL Error 60. This error means that cURL can’t verify the SSL certificate of the remote host. This could be caused by an outdated root certificate list on your system.

To check your current version of cURL via the WordPress admin dashboard, go to Tools → Site Health → Info → Server:

A WordPress Site Health report showing the Server section. The cURL version shown is 8.7.2. (SecureTransport) LibreSSL/3.3.6.

Compare your cURL version to the list of cURL releases. If it’s more than 3 years old, it’s a good idea to upgrade.

Closing Thoughts

Making HTTP requests is a fundamental task, and WordPress relies heavily on this functionality to perform various operations such as updates, plugin downloads, and media uploads. cURL is at the heart of this process.

The PHP implementation of cURL, while powerful, can be complex, which is why libraries like Requests simplify the process, making it more manageable for developers. WordPress leverages the Requests library to handle its HTTP requests, providing a more abstracted and user-friendly interface through functions like wp_remote_get() and wp_remote_post(). This abstraction layer not only simplifies the development process but also ensures that WordPress can efficiently interact with remote servers without the need for manual cURL option handling.

This is an updated version of an article originally written by Peter Tasker.

About the Author

Mike Davey Senior Editor

Mike is an editor and writer based in Hamilton, Ontario, with an extensive background in business-to-business communications and marketing. His hobbies include reading, writing, and wrangling his four children.