Keep the website in curl_init() and set the intermediary on that handle. PHP needs its cURL extension; a working terminal curl executable does not establish that the extension is available.

<?php
$ch = curl_init('https://example.com/');
curl_setopt_array($ch, [
    CURLOPT_PROXY => 'http://203.0.113.10:8080',
    CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
    CURLOPT_NOPROXY => '',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 15,
]);

$body = curl_exec($ch);
if ($body === false) {
    throw new RuntimeException(curl_error($ch), curl_errno($ch));
}

echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), PHP_EOL;
echo $body;

Replace the documentation IP and port with your HTTP proxy or an entry from the HTTP proxy list. CURLOPT_NOPROXY clears bypass rules for this test; CURLOPT_RETURNTRANSFER returns the body instead of sending it to the browser. The connection limit bounds setup and the overall timeout caps the cURL operation.

For a SOCKS5 endpoint, replace the HTTP proxy options before curl_exec():

curl_setopt($ch, CURLOPT_PROXY, '203.0.113.10:1080');
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);

CURLPROXY_SOCKS5_HOSTNAME delegates destination DNS to the SOCKS server. Use the stated protocol; port 1080 alone cannot identify it. Neither proxy choice requires disabling the website's certificate verification.

Test $body === false strictly. An empty body can be a successful transfer, while a tunnel failure can occur before any website response exists. Conversely, a nonempty body can accompany 403, 404 or 429. Read CURLINFO_RESPONSE_CODE alongside cURL's error result to decide whether the completed request serves your task.

Related articles