Table of Contents
cURL is the most powerful HTTP library built into PHP. Every serious PHP developer uses it – scraping websites, integrating APIs, downloading files, posting data to external services. Yet most developers only know three functions: curl_init(), curl_setopt(), and curl_exec().
This guide covers the complete PHP cURL toolkit – every function you actually use, the options that matter, error handling that works in production, and patterns for common tasks. All with working code.
What Is cURL?
cURL is a library for transferring data using URLs. In PHP it’s built-in with the curl extension. It lets your PHP code make HTTP requests – GET, POST, PUT, DELETE, and more. Download files, call APIs, scrape websites, submit forms, send emails via SMTP.
The alternative is PHP streams or Guzzle HTTP client, but PHP cURL is lighter, needs no composer dependencies, and is available on virtually every PHP host. Understanding it properly saves you from writing fragile wrapper functions.
For the official PHP cURL documentation and complete list of functions, see the official PHP cURL manual.
Checking If cURL Is Installed
cURL is usually installed by default but verify:
<?php
// Check if cURL extension is loaded
if (extension_loaded('curl')) {
echo "cURL is installed and enabled.";
} else {
echo "cURL is not available.";
}
// Or check in terminal
php -m | grep curl
Output:
cURL is installed and enabled.
The Basic PHP cURL Workflow
Every cURL request follows the same pattern:
<?php
// Step 1: Initialize a cURL handle
$ch = curl_init();
// Step 2: Set options
curl_setopt($ch, CURLOPT_URL, "https://example.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Step 3: Execute the request
$response = curl_exec($ch);
// Step 4: Check for errors
if (curl_errno($ch)) {
echo "cURL error: " . curl_error($ch);
}
// Step 5: Get info and clean up
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Step 6: Use the response
var_dump($response);
The workflow is always the same. The variations come from which options you set in step 2.
Making a GET Request With PHP cURL
The simplest request – fetch a URL:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.example.com/users");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
die("Request failed: $error");
}
curl_close($ch);
// Parse the response
$data = json_decode($response, true);
var_dump($data);
Output:
array(2) {
[0] => array(2) {
["id"] => 1
["name"] => "John Doe"
}
[1] => array(2) {
["id"] => 2
["name"] => "Jane Smith"
}
}
Making a POST Request With PHP cURL
POST sends data to a server. Used for API calls, form submissions, file uploads:
<?php
$ch = curl_init();
$url = "https://api.example.com/users";
$post_data = json_encode([
"name" => "John Doe",
"email" => "john@example.com",
"role" => "admin"
]);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer YOUR_API_KEY"
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Error: " . curl_error($ch);
} else {
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP Status: $http_code\n";
echo "Response: $response\n";
}
curl_close($ch);
Output:
HTTP Status: 201
Response: {"id":3,"name":"John Doe","email":"john@example.com","role":"admin"}
PHP cURL Options: The Complete Reference
cURL has 200+ options. These are the ones you actually use in production:
| Option | Purpose | Example |
|---|---|---|
| CURLOPT_URL | The URL to request | https://api.example.com |
| CURLOPT_GET | Use GET method | curl_setopt($ch, CURLOPT_GET, true); |
| CURLOPT_POST | Use POST method | curl_setopt($ch, CURLOPT_POST, true); |
| CURLOPT_CUSTOMREQUEST | Use custom HTTP method | “PUT”, “DELETE”, “PATCH” |
| CURLOPT_POSTFIELDS | Data to send in body | json_encode($data) |
| CURLOPT_HTTPHEADER | Custom headers | [“Authorization: Bearer token”] |
| CURLOPT_RETURNTRANSFER | Return response as string | true |
| CURLOPT_TIMEOUT | Timeout in seconds | 10 |
| CURLOPT_CONNECTTIMEOUT | Connection timeout in seconds | 5 |
| CURLOPT_USERAGENT | User-Agent header | “Mozilla/5.0…” |
| CURLOPT_FOLLOWLOCATION | Follow redirects | true |
| CURLOPT_MAXREDIRS | Max redirects to follow | 5 |
| CURLOPT_COOKIE | Send cookies | “name=value; other=data” |
| CURLOPT_COOKIEJAR | Save cookies to file | “/tmp/cookies.txt” |
| CURLOPT_COOKIEFILE | Load cookies from file | “/tmp/cookies.txt” |
| CURLOPT_SSL_VERIFYPEER | Verify SSL certificate | true (always in production) |
| CURLOPT_PROXY | Use a proxy server | “proxy.example.com:8080” |
| CURLOPT_USERPWD | HTTP basic auth | “username:password” |
Error Handling With PHP cURL
Always handle errors. The request might fail and you need to know why:
<?php
function makeCurlRequest($url, $options = []) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
// Apply custom options
foreach ($options as $option => $value) {
curl_setopt($ch, $option, $value);
}
// Execute and capture errors
$response = curl_exec($ch);
if (curl_errno($ch)) {
$error_code = curl_errno($ch);
$error_message = curl_error($ch);
curl_close($ch);
throw new Exception(
"cURL Error [$error_code]: $error_message"
);
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Check HTTP status codes
if ($http_code >= 400) {
throw new Exception(
"HTTP Error: $http_code - $response"
);
}
return $response;
}
// Usage
try {
$response = makeCurlRequest("https://api.example.com/users");
echo "Success: $response\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Handling JSON APIs With PHP cURL
Most modern APIs use JSON. Pattern for calling them safely:
<?php
class APIClient {
private $base_url;
private $api_key;
public function __construct($base_url, $api_key) {
$this->base_url = $base_url;
$this->api_key = $api_key;
}
public function get($endpoint) {
return $this->request($endpoint, 'GET', null);
}
public function post($endpoint, $data) {
return $this->request($endpoint, 'POST', $data);
}
private function request($endpoint, $method, $data) {
$ch = curl_init();
$url = $this->base_url . $endpoint;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
// Headers for JSON API
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Content-Type: application/json",
"Authorization: Bearer " . $this->api_key
]);
// Include POST data if provided
if ($data) {
curl_setopt($ch, CURLOPT_POSTFIELDS,
json_encode($data)
);
}
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
// Decode JSON response
$decoded = json_decode($response, true);
if ($http_code >= 400) {
throw new Exception(
"API Error [$http_code]: " .
($decoded['error'] ?? $response)
);
}
return $decoded;
}
}
// Usage
$api = new APIClient("https://api.example.com", "your_api_key");
try {
// GET request
$users = $api->get("/users");
var_dump($users);
// POST request
$new_user = $api->post("/users", [
"name" => "John Doe",
"email" => "john@example.com"
]);
var_dump($new_user);
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Downloading Files With PHP cURL
<?php
function downloadFile($url, $save_path) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$content = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code != 200) {
throw new Exception("Failed to download: HTTP $http_code");
}
// Save to file
file_put_contents($save_path, $content);
return $save_path;
}
// Download an image
try {
$file = downloadFile(
"https://example.com/image.jpg",
"/tmp/downloaded_image.jpg"
);
echo "Downloaded to: $file";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Getting Response Headers With PHP cURL
<?php
function getResponseHeaders($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true); // Include headers in output
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
// Separate headers from body
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);
return [
'headers' => $headers,
'body' => $body
];
}
$result = getResponseHeaders("https://api.example.com/status");
echo "Headers:\n" . $result['headers'];
echo "Body:\n" . $result['body'];
Retrying Failed Requests
Network requests fail. Retry logic saves unreliable connections:
<?php
function requestWithRetry($url, $max_retries = 3,
$retry_delay = 2) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$attempt = 0;
while ($attempt < $max_retries) {
$attempt++;
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Success
if ($http_code == 200) {
curl_close($ch);
return $response;
}
// Server error - retry
if ($http_code >= 500) {
if ($attempt < $max_retries) {
echo "Attempt $attempt failed with HTTP $http_code. ";
echo "Retrying in {$retry_delay}s...\n";
sleep($retry_delay);
continue;
}
}
// Client error - don't retry
if ($http_code >= 400 && $http_code < 500) {
curl_close($ch);
throw new Exception(
"HTTP $http_code - Client error: $response"
);
}
}
curl_close($ch);
throw new Exception(
"Failed after $max_retries attempts"
);
}
// Usage
try {
$response = requestWithRetry(
"https://api.example.com/users",
3, // retry 3 times
2 // wait 2 seconds between retries
);
echo "Success: $response";
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
PHP cURL vs Alternatives
| Tool | Built-in | Best For | Drawback |
|---|---|---|---|
| PHP cURL | Yes | Simple requests, no dependencies | Lower-level API |
| Guzzle | No (composer) | Complex API workflows | Dependency overhead |
| PHP Streams | Yes | File operations | Limited HTTP features |
| Symfony HttpClient | No (composer) | Modern frameworks | Framework-specific |
For most projects: start with PHP cURL. Add Guzzle only if you need the abstraction for managing many requests or want middleware chains.
Frequently Asked Questions
What is PHP cURL?
PHP cURL is a built-in PHP extension that transfers data using URLs – making HTTP requests, downloading files, posting to APIs, scraping websites. It’s built on the libcurl library and available on nearly all PHP installations.
What is the difference between CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT?
CURLOPT_CONNECTTIMEOUT is how long to wait for the initial connection to the server – typically 5 seconds. CURLOPT_TIMEOUT is the total time for the entire operation including data download – typically 10-30 seconds. Set both. Connection hangs need quick failure. Slow downloads need longer tolerance.
Should I set CURLOPT_SSL_VERIFYPEER to false?
Never in production. Setting CURLOPT_SSL_VERIFYPEER = false disables SSL certificate verification – useful for testing with self-signed certificates but dangerous in production where it opens the door to man-in-the-middle attacks. If a legitimate certificate is failing to verify, fix the cert chain, not the verification.
How do I handle redirects with cURL?
Set CURLOPT_FOLLOWLOCATION = true and optionally CURLOPT_MAXREDIRS = 5. cURL will automatically follow redirect responses (HTTP 301, 302, 303, 307) and give you the final response. Without these options redirects return the redirect response itself, not the target.
Can I reuse a cURL handle for multiple requests?
Yes. Reset the options you changed with curl_reset($ch) before making another request, or close it with curl_close($ch) and create a new one. Creating new handles is safer for different requests because you avoid accidentally inheriting options from the previous request.
How do I send files with cURL?
Use the @ prefix with CURLOPT_POSTFIELDS:
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'file' => new CURLFile('/path/to/file.pdf'),
'description' => 'My file'
]);
Summary
PHP cURL is the standard tool for HTTP requests in PHP. The workflow is simple:
- curl_init() – create a handle
- curl_setopt() – configure it
- curl_exec() – execute and get response
- Check curl_errno() – handle errors
- curl_getinfo() – get metadata
- curl_close() – clean up
Master this and you can call any API, download any file, or scrape any public website from PHP. For specific scraping scenarios see the PHP cURL web scraping guide with working scrapers. For API integration see the PHP OpenAI API chatbot guide.
