ScreenshotNeo

BlogHow-to

How to Use Proxies With PHP Guzzle

Configure HTTP and HTTPS proxies in Guzzle, add authentication, bypass selected hosts, and avoid common TLS and credential leaks.

By the ScreenshotNeo team30 September 20269 min read

How to Use Proxies With PHP Guzzle

To use a proxy with Guzzle, set the proxy request option to a proxy URI, or to an array with separate http, https, and optional no entries. Put the option in the client constructor for a shared default, or in an individual request for one-off routing. Keep TLS certificate verification enabled, protect proxy credentials, and check the installed Guzzle and libcurl versions before using HTTPS proxies or proxy authorization headers.

This guide covers Guzzle 7-style client usage. The examples send requests to https://example.com; replace the proxy host, port, credentials, and destination with values for your environment. See the Guzzle proxy request option documentation.

1. Configure a proxy in Guzzle

Guzzle accepts a string proxy URI for all protocols, or an associative array to route HTTP and HTTPS destinations separately. The array form can include a no list for hosts that must connect directly.

Guzzle can route by destination scheme and send selected hosts directly using the no bypass list.
Guzzle can route by destination scheme and send selected hosts directly using the no bypass list.
<?php
require __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
    'proxy' => [
        'http'  => 'http://proxy.example:8080',
        'https' => 'http://proxy.example:8080',
        'no'    => ['localhost', '127.0.0.1', '.internal.example'],
    ],
    'timeout' => 30,
    'connect_timeout' => 10,
    'verify' => true,
]);

$response = $client->request('GET', 'https://example.com');
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();

Install Guzzle in a Composer project with composer require guzzlehttp/guzzle, save the snippet as a PHP file, and run it from the command line. The code assumes the proxy accepts ordinary HTTP proxy connections. A destination URL starting with HTTPS can still use an HTTP proxy: the client typically asks that proxy to establish a tunnel to the destination.

One proxy for every destination

$client = new Client([
    'proxy' => 'http://proxy.example:8080',
]);

Use the string form when the same proxy should handle both HTTP and HTTPS destinations and you do not need a bypass list. For different proxy endpoints per destination scheme or selected direct routes, use the array form.

Client defaults or a single request

Client-level defaults make every request from that client use the proxy unless overridden. They are convenient for a worker or service dedicated to one route. Guzzle clients are immutable after creation; create another client when you need a different set of defaults.

$client = new Client(['timeout' => 30]);

$response = $client->request('GET', 'https://example.com', [
    'proxy' => 'http://proxy.example:8080',
]);

Request-level options are useful when only some destinations should use the proxy. Avoid sharing a proxy-configured client with requests that need a different security boundary unless each request explicitly sets and reviews its routing options.

2. Add proxy authentication safely

For basic credentials supported by the proxy, Guzzle permits username and password in the proxy URI:

$proxy = 'http://' . rawurlencode($proxyUser) . ':'
    . rawurlencode($proxyPassword) . '@proxy.example:8080';

$client = new Client(['proxy' => $proxy]);

Read secrets from a protected environment variable or secret manager rather than committing them to source control. URL-encode credentials so reserved characters such as @, :, /, and # are not parsed as URI syntax. Never print the complete proxy URI in logs or error reports; redact its user information before recording configuration.

Do not assume every proxy uses Basic authentication or that every Guzzle handler supports every authentication mechanism in the same way. Confirm the proxy’s requirements and the selected transport handler. If using cURL-specific proxy credentials, configure them through the cURL handler’s supported options and verify behavior in the deployed runtime.

Be especially careful with a first-class Proxy-Authorization request header. Guzzle versions before 7.14.2 could expose this header to an origin server in some direct, bypass, SOCKS, or redirect scenarios. The Guzzle advisory recommends upgrading to 7.14.2 or later. If upgrading is temporarily impossible, avoid first-class proxy authorization headers; the advisory describes proxy URI userinfo or CURLOPT_PROXYUSERPWD with cURL handlers as workarounds. Review the Guzzle Proxy-Authorization security advisory and audit redirects and bypass routes.

3. Bypass the proxy for selected hosts

Use the no array entry to list destinations that should not go through the configured proxy:

'proxy' => [
    'http'  => 'http://proxy.example:8080',
    'https' => 'http://proxy.example:8080',
    'no'    => [
        'localhost',
        '127.0.0.1',
        '.internal.example',
    ],
],

Include the exact hosts and domain patterns your application actually uses. Test localhost, internal names, and any important subdomains independently; a bypass mistake can send private traffic to an external proxy or route a request directly when you expected it to be mediated. Do not infer that a particular wildcard or suffix rule works as intended without testing it against the Guzzle version and handler you deploy.

Guzzle also reads HTTP_PROXY for HTTP requests, HTTPS_PROXY for HTTPS requests, and NO_PROXY for bypass destinations. On CLI SAPI, configure them in the process environment:

export HTTP_PROXY='http://proxy.example:8080'
export HTTPS_PROXY='http://proxy.example:8080'
export NO_PROXY='localhost,127.0.0.1,.internal.example'
php fetch.php

Guzzle documents that HTTP_PROXY is read only in CLI SAPI because untrusted CGI input can create HTTPoxy-style behavior. If you pass an explicit proxy option, provide its no entries yourself when you need the environment’s exclusions; do not assume an explicit proxy array will automatically reuse NO_PROXY. See Guzzle’s proxy option notes.

4. Choose the right proxy and TLS configuration

Configuration Use it for Check
One proxy URI string Same endpoint for HTTP and HTTPS destinations Does the endpoint accept both traffic types?
http and https entries Different routing by destination scheme Test each scheme with its own endpoint
no list Direct connections for specific hosts Test every hostname and suffix pattern
Environment variables Deployment-level defaults Inspect the actual process environment and exclusions
https:// proxy URI TLS-encrypted connection from client to proxy Guzzle and libcurl must support HTTPS proxies

Keep verify at its default true. That verifies the destination server certificate; use a trusted CA bundle path if your runtime does not have the needed CA certificates. Setting verify => false disables certificate validation and is insecure. A proxy does not remove the need to validate the destination. If your proxy performs TLS inspection, configure the appropriate trusted CA according to your organization’s security policy rather than disabling verification. See Guzzle’s verify option documentation.

An HTTPS proxy protects the client-to-proxy leg, while destination certificate verification still matters.
An HTTPS proxy protects the client-to-proxy leg, while destination certificate verification still matters.

An https:// proxy URI means the connection from your client to the proxy uses TLS. It is different from requesting an HTTPS destination through an ordinary HTTP proxy. Guzzle 7.12.1 and later detect whether the installed libcurl supports HTTPS proxies and reject unsupported first-class configurations; libcurl support requires version 7.52.0 or newer with the HTTPS-proxy feature. Older Guzzle versions combined with some older libcurl builds could silently treat an HTTPS proxy as plaintext. Upgrade and inspect the HTTPS-proxy downgrade advisory before using this scheme.

5. Diagnose routing and transport issues

  1. Confirm the runtime. Check composer show guzzlehttp/guzzle, whether PHP’s cURL extension is loaded, and the libcurl version and features. The handler can affect supported options and proxy behavior.
  2. Check the effective route. Inspect proxy scheme and host without exposing credentials. Environment variables, client defaults, and request options may all affect the route.
  3. Test HTTP and HTTPS separately. A successful HTTP request does not prove that the HTTPS entry, CONNECT tunneling, or proxy TLS works.
  4. Test bypass behavior. Run requests to every configured direct host and representative proxied host, including redirects if your application follows them.
  5. Keep certificate checks on. Install or configure the required CA bundle when verification fails; do not treat disabling verification as a fix.
  6. Review credentials and redirects. Ensure proxy credentials cannot appear in origin-bound headers, logs, traces, or error output. Upgrade before using first-class Proxy-Authorization headers.
  7. Validate destination hosts. Proxy routing is not SSRF protection. Validate URLs and every redirect destination against your application’s own allowlist and network policy. Guzzle’s noncanonical-host advisory lists patched releases 7.15.2 and 8.0.1; review it when accepting untrusted URLs.

Common errors and fixes

Symptom Likely cause Fix
Connection refused or timeout Wrong proxy host/port, network ACL, or unreachable proxy Verify the endpoint from the same machine/container and inspect firewall rules.
407 Proxy Authentication Required Missing, incorrect, or unsupported proxy credentials Confirm authentication type and secret values; use the supported method for the chosen handler.
Certificate verification failure Missing CA bundle, interception CA not trusted, or invalid certificate chain Install/configure a trusted CA bundle and leave verify enabled.
HTTPS proxy rejected or behaves like plaintext Unsupported Guzzle/libcurl combination Use Guzzle 7.12.1 or later and confirm libcurl HTTPS-proxy support.
A host unexpectedly connects directly It matches no/NO_PROXY or an explicit option overrides defaults Review the effective bypass list and test the exact hostname.
Proxy credentials appear at an origin Old Guzzle version and first-class Proxy-Authorization header, often involving a redirect or bypass Upgrade to 7.14.2 or later, remove the header from request defaults and middleware, and rotate exposed credentials.
Environment proxy has no effect Wrong variable for the request scheme, non-CLI SAPI, or explicit proxy configuration Set the appropriate variable in the process environment; configure options explicitly when needed.

6. Performance, reliability, and cost

A proxy adds another network hop, so connection establishment and response time depend on both the proxy and destination. Reuse a Guzzle client for requests sharing the same configuration, set finite connect and total timeouts, and make retries deliberate. Retry only failures that are safe for the operation; a timeout can happen after a server has processed a request. For non-idempotent requests, use application-level idempotency controls where available.

Proxy reliability is a dependency question: monitor connection failures, latency, authentication errors, and proxy-side limits. A proxy may be unavailable even while the destination is healthy. Decide whether failure should stop the request or use a separately approved fallback route; do not silently bypass a proxy that enforces access controls or egress policy.

Costs depend on the proxy provider’s pricing model and usage terms. Track request volume and egress, and avoid retries that amplify traffic during an outage. This guide does not recommend a proxy vendor; no provider or affiliate program was verified for this research.

7. Capture a screenshot without managing a browser

For page screenshots, you may not need to run a browser yourself. ScreenshotNeo is a website screenshot API and MCP server. It is separate from Guzzle proxy configuration: use its API when the task is to capture a page as an image or PDF, rather than route arbitrary PHP HTTP requests through your proxy. See the ScreenshotNeo documentation.

<?php
$ch = curl_init();
$query = http_build_query([
    'access_key' => getenv('SCREENSHOTNEO_API_KEY'),
    'url' => 'https://stripe.com',
]);
curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.screenshotneo.com/v1/shot?' . $query,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 90,
]);
$image = curl_exec($ch);
if ($image === false) {
    throw new RuntimeException(curl_error($ch));
}
file_put_contents('shot.webp', $image);
curl_close($ch);

The equivalent one-call examples are:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up free and get 1,000 screenshots a month with no card.

8. FAQ

Does Guzzle support SOCKS proxies?

Support depends on the transport handler and its underlying library. Verify the selected handler’s supported proxy schemes and the installed cURL/libcurl capabilities before deployment; do not assume every URI scheme works with every handler.

Will a proxy protect my application from SSRF?

No. Validate user-supplied destinations and redirects against an explicit host and network policy. A proxy controls routing; it is not an input-validation boundary.

Should I put proxy configuration in PHP source?

Non-secret routing defaults can live in application configuration. Credentials should come from protected runtime configuration or a secret manager, and must be redacted from logs.

Can I change the proxy on an existing Guzzle client?

Client defaults are immutable after construction. Build a separate client with the desired proxy settings, or pass a request-level option for an isolated call.

Quick reference

  • Use the proxy string form for one endpoint, or the http/https/no array for explicit routing.
  • Use HTTP_PROXY, HTTPS_PROXY, and NO_PROXY for CLI process defaults; provide bypass entries yourself when setting an explicit proxy array.
  • Keep verify enabled and use a trusted CA bundle when needed.
  • Use Guzzle 7.12.1 or later for HTTPS proxy handling and 7.14.2 or later before relying on first-class Proxy-Authorization headers; review current advisories before deployment.
  • Validate destination hosts independently of proxy routing.