WordPress HTTP API Overview
The WordPress HTTP API provides a standardized interface for making HTTP requests. It abstracts away the complexity of different PHP configurations and provides a consistent, secure way to communicate with external services.
Architecture
wp_remote_*() functions
↓
WP_Http class
↓
WpOrgRequestsRequests (Requests library)
↓
Transport Layer (cURL or Streams)
Request Flow
1. Entry Point
All requests start via global functions:
// Standard requests
wp_remote_request( $url, $args );
wp_remote_get( $url, $args );
wp_remote_post( $url, $args );
wp_remote_head( $url, $args );
// Safe requests (for user-controlled URLs)
wp_safe_remote_request( $url, $args );
wp_safe_remote_get( $url, $args );
wp_safe_remote_post( $url, $args );
wp_safe_remote_head( $url, $args );
2. WP_Http Singleton
Functions access a singleton WP_Http instance via _wp_http_get_object():
function _wp_http_get_object() {
static $http = null;
if ( is_null( $http ) ) {
$http = new WP_Http();
}
return $http;
}
3. Request Processing
The WP_Http::request() method:
- Merges arguments with defaults
- Applies
http_request_argsfilter - Checks
pre_http_requestfilter for short-circuit - Validates URL (if
reject_unsafe_urlsis true) - Checks if request is blocked (
WP_HTTP_BLOCK_EXTERNAL) - Configures SSL, proxy, cookies
- Delegates to Requests library
- Converts response to WordPress format
- Applies
http_responsefilter
4. Transport Selection
Since WordPress 4.6, the Requests library handles transport selection:
| Priority | Transport | Requirements |
|---|---|---|
| 1 | cURL | curl_init() and curl_exec() functions |
| 2 | PHP Streams | stream_socket_client() function |
The legacy WP_Http_Curl and WP_Http_Streams classes are deprecated as of WordPress 6.4.
Response Format
Successful requests return an array:
[
'headers' => CaseInsensitiveDictionary, // Response headers
'body' => string, // Response body
'response' => [
'code' => int, // HTTP status code
'message' => string, // Status message
],
'cookies' => WP_Http_Cookie[], // Cookie objects
'filename' => string|null, // For streamed responses
'http_response' => WP_HTTP_Requests_Response, // Raw response object
]
Failed requests return a WP_Error object.
Transports
cURL Transport (WP_Http_Curl)
- Deprecated: 6.4.0
- Uses PHP’s cURL extension
- Supports all HTTP methods
- Handles SSL verification
- Supports proxy authentication (including NTLM)
- Streams large responses efficiently
Streams Transport (WP_Http_Streams)
- Deprecated: 6.4.0
- Uses
stream_socket_client() - Fallback when cURL unavailable
- Supports SSL with OpenSSL
- Custom certificate validation
Security Features
URL Validation
The wp_http_validate_url() function protects against SSRF attacks:
- Only allows
httpandhttpsprotocols - Blocks local/private IP ranges (unless explicitly allowed)
- Restricts ports to 80, 443, 8080 (configurable)
- Rejects URLs with credentials
// Safe for user input
wp_safe_remote_get( $_POST['url'] ); // Validates URL
// Only for trusted URLs
wp_remote_get( 'https://api.example.com/data' );
Request Blocking
Block all external requests via wp-config.php:
define( 'WP_HTTP_BLOCK_EXTERNAL', true );
define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,*.github.com' );
SSL Verification
SSL is verified by default using bundled CA certificates:
wp_remote_get( $url, [
'sslverify' => true, // Default
'sslcertificates' => '/path/to/ca-bundle.crt' // Custom CA
] );
Caching
The HTTP API itself doesn’t implement caching, but WordPress caches some requests:
Transient-Based Caching Pattern
function get_api_data() {
$cache_key = 'my_api_data';
$data = get_transient( $cache_key );
if ( false === $data ) {
$response = wp_remote_get( 'https://api.example.com/data' );
if ( ! is_wp_error( $response ) ) {
$data = wp_remote_retrieve_body( $response );
set_transient( $cache_key, $data, HOUR_IN_SECONDS );
}
}
return $data;
}
HTTP Caching Headers
The API respects standard HTTP caching:
Cache-ControlheadersETag/If-None-MatchLast-Modified/If-Modified-Since
Plugins can implement conditional requests:
$response = wp_remote_get( $url, [
'headers' => [
'If-None-Match' => $stored_etag,
],
] );
if ( 304 === wp_remote_retrieve_response_code( $response ) ) {
// Use cached version
}
Compression
The API automatically handles compressed responses:
Supported Encodings
| Encoding | Function | Priority |
|---|---|---|
| deflate | gzinflate() |
1.0 |
| compress | gzuncompress() |
0.5 |
| gzip | gzdecode() |
0.5 |
Automatic Decompression
// Decompress is enabled by default
wp_remote_get( $url, [
'decompress' => true, // Default
] );
// Disable to get raw compressed data
wp_remote_get( $url, [
'decompress' => false,
] );
Cookie Handling
Cookies are automatically parsed and made available:
$response = wp_remote_get( $url );
$cookies = wp_remote_retrieve_cookies( $response );
foreach ( $cookies as $cookie ) {
echo $cookie->name . ': ' . $cookie->value;
}
Send cookies with requests:
wp_remote_get( $url, [
'cookies' => [
'session_id' => 'abc123',
new WP_Http_Cookie( [
'name' => 'auth',
'value' => 'token',
'expires' => time() + 3600,
] ),
],
] );
Proxy Support
Configure via wp-config.php:
define( 'WP_PROXY_HOST', '192.168.1.100' );
define( 'WP_PROXY_PORT', '8080' );
define( 'WP_PROXY_USERNAME', 'user' );
define( 'WP_PROXY_PASSWORD', 'pass' );
define( 'WP_PROXY_BYPASS_HOSTS', 'localhost, *.local' );
Timeouts and Limits
wp_remote_get( $url, [
'timeout' => 5, // Seconds (default: 5)
'redirection' => 5, // Max redirects (default: 5)
'limit_response_size' => 1048576, // Bytes (1MB)
] );
Streaming to File
For large downloads:
wp_remote_get( $large_file_url, [
'stream' => true,
'filename' => '/tmp/download.zip',
] );
Non-Blocking Requests
Fire-and-forget requests:
wp_remote_post( $webhook_url, [
'blocking' => false,
'body' => [ 'event' => 'completed' ],
] );
// Returns immediately, no response body
HTTP Status Code Constants
WP_Http provides status code constants:
WP_Http::OK // 200
WP_Http::CREATED // 201
WP_Http::MOVED_PERMANENTLY // 301
WP_Http::FOUND // 302
WP_Http::NOT_MODIFIED // 304
WP_Http::BAD_REQUEST // 400
WP_Http::UNAUTHORIZED // 401
WP_Http::FORBIDDEN // 403
WP_Http::NOT_FOUND // 404
WP_Http::INTERNAL_SERVER_ERROR // 500
// ... and many more
Best Practices
-
Use safe functions for user input: Always use
wp_safe_remote_*()for URLs from untrusted sources -
Set appropriate timeouts: Don’t use excessively long timeouts that could block execution
-
Handle errors: Always check for
WP_Error:$response = wp_remote_get( $url ); if ( is_wp_error( $response ) ) { error_log( $response->get_error_message() ); return; } -
Validate response codes:
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { // Handle non-200 response } -
Implement caching: Use transients or object cache for repeated requests
-
Don’t disable SSL verification in production:
// NEVER do this in production 'sslverify' => false // Security risk!
Related Files
wp-includes/http.php– API functionswp-includes/class-wp-http.php– Main classwp-includes/class-wp-http-cookie.php– Cookie handlingwp-includes/class-wp-http-encoding.php– Compressionwp-includes/class-wp-http-proxy.php– Proxy supportwp-includes/class-wp-http-response.php– Response objectswp-includes/Requests/– Requests library