PHP cURL: The Best Complete Guide to HTTP Requests in 2026

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:

OptionPurposeExample
CURLOPT_URLThe URL to requesthttps://api.example.com
CURLOPT_GETUse GET methodcurl_setopt($ch, CURLOPT_GET, true);
CURLOPT_POSTUse POST methodcurl_setopt($ch, CURLOPT_POST, true);
CURLOPT_CUSTOMREQUESTUse custom HTTP method“PUT”, “DELETE”, “PATCH”
CURLOPT_POSTFIELDSData to send in bodyjson_encode($data)
CURLOPT_HTTPHEADERCustom headers[“Authorization: Bearer token”]
CURLOPT_RETURNTRANSFERReturn response as stringtrue
CURLOPT_TIMEOUTTimeout in seconds10
CURLOPT_CONNECTTIMEOUTConnection timeout in seconds5
CURLOPT_USERAGENTUser-Agent header“Mozilla/5.0…”
CURLOPT_FOLLOWLOCATIONFollow redirectstrue
CURLOPT_MAXREDIRSMax redirects to follow5
CURLOPT_COOKIESend cookies“name=value; other=data”
CURLOPT_COOKIEJARSave cookies to file“/tmp/cookies.txt”
CURLOPT_COOKIEFILELoad cookies from file“/tmp/cookies.txt”
CURLOPT_SSL_VERIFYPEERVerify SSL certificatetrue (always in production)
CURLOPT_PROXYUse a proxy server“proxy.example.com:8080”
CURLOPT_USERPWDHTTP 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

ToolBuilt-inBest ForDrawback
PHP cURLYesSimple requests, no dependenciesLower-level API
GuzzleNo (composer)Complex API workflowsDependency overhead
PHP StreamsYesFile operationsLimited HTTP features
Symfony HttpClientNo (composer)Modern frameworksFramework-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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top