Back to Guzzle

Request Options

docs/request-options.md

8.0.091.5 KB
Original Source

Request Options

You can customize requests created and transferred by a client using request options. Request options control various aspects of a request including, headers, query string parameters, timeout settings, the body of a request, and much more.

All of the following examples use the following client:

php
$client = new GuzzleHttp\Client(['base_uri' => 'http://httpbin.org']);

allow_redirects

Summary Describes the redirect behavior of a request

Types

  • bool
  • array

Default

php
[
    'max'             => 5,
    'strict'          => false,
    'referer'         => false,
    'protocols'       => ['http', 'https'],
    'track_redirects' => false,
]

Constant GuzzleHttp\RequestOptions::ALLOW_REDIRECTS

Set to false to disable redirects.

php
$res = $client->request('GET', '/redirect/3', ['allow_redirects' => false]);
echo $res->getStatusCode();
// 302

Set to true (the default setting) to enable normal redirects with a maximum number of 5 redirects.

php
$res = $client->request('GET', '/redirect/3');
echo $res->getStatusCode();
// 200

You can also pass an associative array containing the following key value pairs:

  • max: (int, default=5) maximum number of allowed redirects.

  • strict: (bool, default=false) Set to true to use strict redirects. Strict RFC compliant redirects mean that POST redirect requests are sent as POST requests vs. doing what most browsers do which is redirect POST requests with GET requests. The RFC 10008 QUERY method keeps its method and body across non-strict 301 and 302 redirects, matching the 307 and 308 behavior that already applies to every method, and a 303 redirect is followed with a body-less GET. When redirect handling clears the body, it also removes Content-Length and Transfer-Encoding.

  • referer: (bool, default=false) Set to true to add a Referer header when redirecting. On a cross-origin redirect only the origin (scheme, host, and port) is sent, and the header is omitted entirely when the scheme changes, including an https to http downgrade. Generated values never include user information or fragments. See Cross-Origin Redirects.

  • protocols: (non-empty array containing http and/or https, default=['http', 'https']) Specifies which protocols are allowed for redirect requests. Values are case-sensitive; only http and https are accepted.

  • on_redirect: (callable) PHP callable that is invoked when a redirect is encountered. The callable is invoked with the original request, the redirect response that was received, and the effective URI. Any return value from the on_redirect function is ignored. When requests are sent with GuzzleHttp\Pool and this callback is supplied via the pool's options configuration, the callable also receives the iterable key that identified the request as a fourth argument.

  • track_redirects: (bool) When set to true, each redirected URI and status code encountered will be tracked in the X-Guzzle-Redirect-History and X-Guzzle-Redirect-Status-History headers respectively. All URIs and status codes will be stored in the order which the redirects were encountered.

[!NOTE] When tracking redirects the X-Guzzle-Redirect-History header will exclude the initial request's URI and the X-Guzzle-Redirect-Status-History header will exclude the final status code. Redirect history is stored in response headers, and those header names are not reserved by Guzzle. If the final response already contains headers with these names, including when no redirect occurs, those values may be server-provided. Do not use these headers as a security boundary. For security-sensitive redirect history, collect values with the on_redirect option instead.

[!NOTE] Guzzle follows only the redirect status codes 301, 302, 303, 307, and 308. Other 3xx responses are returned as-is even when they include a Location header.

php
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;

$onRedirect = function(
    RequestInterface $request,
    ResponseInterface $response,
    UriInterface $uri
) {
    echo 'Redirecting! ' . $request->getUri() . ' to ' . $uri . "\n";
};

$res = $client->request('GET', '/redirect/3', [
    'allow_redirects' => [
        'max'             => 10,        // allow at most 10 redirects.
        'strict'          => true,      // use "strict" RFC compliant redirects.
        'referer'         => true,      // add a Referer header
        'protocols'       => ['https'], // only allow https URLs
        'on_redirect'     => $onRedirect,
        'track_redirects' => true
    ]
]);

echo $res->getStatusCode();
// 200

echo $res->getHeaderLine('X-Guzzle-Redirect-History');
// http://first-redirect, http://second-redirect, etc...

echo $res->getHeaderLine('X-Guzzle-Redirect-Status-History');
// 301, 302, etc...

[!WARNING] This option only has an effect if your handler has the GuzzleHttp\Middleware::redirect middleware. This middleware is added by default when a client is created with no handler, and is added by default when creating a handler with GuzzleHttp\HandlerStack::create.

[!NOTE] This option has no effect when making requests using GuzzleHttp\Client::sendRequest(). In order to stay compliant with PSR-18 any redirect response is returned as is.

Cross-Origin Redirects

Guzzle considers a redirect cross-origin when the scheme, host, or effective port changes.

On cross-origin redirects, Guzzle removes origin-scoped HTTP credentials before sending the redirected request. This includes the Authorization and Cookie headers, the generic auth request option, and cURL HTTP authentication options such as CURLOPT_HTTPAUTH and CURLOPT_USERPWD.

Same-origin redirects preserve those values. Guzzle does not automatically remove transport identity or TLS client credential options solely because the redirect is cross-origin. In particular, TLS client authentication options such as cert, ssl_key, custom cURL TLS options, and stream context TLS options are not removed automatically on cross-origin redirects.

When the optional referer setting is enabled, Guzzle also limits what it discloses to the new origin. On a cross-origin redirect it sends only the request's origin (scheme, host, and port) in the Referer header instead of the path and query, and it omits the header entirely when the scheme changes, including an https to http downgrade. Same-origin redirects preserve the path and query. Generated values never include user information or fragments. This matches the strict-origin-when-cross-origin policy that modern browsers use by default.

If TLS client credentials are only trusted for the original origin, disable automatic redirects and handle redirect responses manually, or use separate clients and request options for trusted origins.

The RFC 10008 QUERY method has method-specific redirect behavior. Guzzle keeps the QUERY method and request body across 301, 302, 307, and 308 redirects, and follows a 303 with a body-less GET. QUERY request bodies can carry sensitive query content. On cross-origin redirects Guzzle removes origin credentials such as the Authorization and Cookie headers and the auth request option, but it does not remove the request body. Disable automatic redirects or use on_redirect if a QUERY body must not be sent to another origin.

auth

Summary Pass HTTP authentication parameters to use with the request. An array must contain the username in index [0], the password in index [1], and can optionally provide a built-in authentication type in index [2]. Pass false or null to disable authentication for a request. String values are passed through for custom handlers.

Built-in Basic and Digest authentication are applied by GuzzleHttp\Middleware::auth(), which is included by default when using GuzzleHttp\HandlerStack::create() or when the client creates its default handler. Raw custom handlers must be wrapped in GuzzleHttp\HandlerStack::create($handler) or explicitly include GuzzleHttp\Middleware::auth() to use built-in Basic or Digest authentication. Unrecognized array auth types are left in the request options for custom middleware or custom handlers.

Types

  • array
  • string
  • false
  • null

Default None

Constant GuzzleHttp\RequestOptions::AUTH

The built-in authentication types are as follows:

basic Use basic HTTP authentication in the Authorization header (the default setting used if none is specified).

Built-in Basic usernames must not contain a colon, and neither Basic usernames nor passwords may contain ASCII control characters. Basic passwords may contain colons.

php
$client->request('GET', '/get', ['auth' => ['username', 'password']]);

digest Use digest authentication through Guzzle's auth middleware. When no reusable challenge is available, Digest authentication sends an initial unauthenticated probe, processes a WWW-Authenticate: Digest ... challenge, and retries with an Authorization: Digest ... header. Requests with a body are probed with an empty body and Content-Length: 0; the body is sent only on authenticated attempts: once in the normal one-challenge handshake, and again if a stale-nonce challenge must be retried. When challenge reuse is enabled (the default) and a cached challenge applies, later body-less requests send the first Digest leg preemptively, with an incremented nonce count; body-bearing requests always keep the probe handshake. The delay option applies before the first Digest leg, whether that leg is a probe or a preemptive request. A non-401 answer to a body-withholding probe other than a proxy challenge (407) or a followable redirect fails the request with ResponseException.

php
$client->request('GET', '/get', [
    'auth' => ['username', 'password', 'digest']
]);

When a Digest challenge omits domain, RFC 7616 scopes the protection space to the whole origin. Do not rely on default reuse across same-origin trust boundaries: a preemptive request can disclose the Digest username or userhash and password-derived response material for the cached realm to another same-origin endpoint before that endpoint challenges. When domain is present, Guzzle applies RFC literal-prefix matching to request targets, so domain="/api" also covers /api-internal, and a query-scoped prefix such as /api?tenant=a also covers longer targets beginning with that same string, such as /api?tenant=abc. Use separate origins, configure a narrow server domain, or disable reuse by replacing the default auth middleware:

php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;

$stack = HandlerStack::create();
$stack->remove('auth');
$stack->after('allow_redirects', Middleware::auth(false), 'auth');

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

Supported Digest algorithms are MD5, MD5-sess, SHA-256, SHA-256-sess, and the SHA-512-256 variants when PHP supports the sha512/256 hash algorithm. Guzzle uses PHP's FIPS SHA-512/256, matching libcurl builds with SHA-512/256 support; servers built against RFC 7616's erratum test vectors for truncated SHA-512 will not interoperate with either Guzzle or curl. Guzzle supports legacy non-session challenges without qop and challenges with qop=auth. Session algorithms require qop. auth-int is not supported.

Each Digest leg is a separate Guzzle handler invocation with its own request options. on_stats fires once per leg; on_headers and progress are also attached per leg. The delay option applies once, before the first Digest leg: the probe, or a preemptive request when challenge reuse applies.

When multiple usable Digest challenges are present, Guzzle selects the strongest supported algorithm, preferring SHA-512-256 over SHA-256 over MD5, and preferring -sess variants within a family. If a challenge offers only auth-int, uses a session algorithm without qop, uses an unknown or unavailable algorithm, is malformed, or contains values that cannot be safely placed in a header, Guzzle ignores that challenge. If no usable Digest challenge remains, the original 401 response is returned for normal http_errors handling; inspect its WWW-Authenticate header to debug the failure.

Proxy-Authenticate Digest challenges are not handled by the auth middleware. A 407 response to a Digest probe passes through unchanged. The built-in cURL handlers allow proxy credentials through CURLOPT_PROXYUSERPWD, but they do not expose CURLOPT_PROXYAUTH through the raw cURL option allow-list, and libcurl defaults proxy authentication to Basic.

Probe redirects are followed by the redirect middleware under normal redirect rules. Non-strict 301/302 and 303 redirects clear the body, with exact GET, HEAD, and OPTIONS keeping their method and other methods rewritten to GET, except exact QUERY on non-strict 301/302 preserves the method and body. 307/308 and strict 301/302 redirects preserve the method and body. A redirected body-bearing request performs its own Digest handshake at the new URI.

To use libcurl's native Digest implementation instead, omit auth and configure cURL options such as CURLOPT_HTTPAUTH => CURLAUTH_DIGEST and CURLOPT_USERPWD directly with a cURL handler.

body

Summary The body option is used to control the body of an entity enclosing request (e.g., PUT, POST, PATCH).

Types

  • string
  • fopen() resource
  • Psr\Http\Message\StreamInterface
  • callable object or closure
  • Iterator
  • Stringable
  • null

Default None

Constant GuzzleHttp\RequestOptions::BODY

This setting can be set to any of the following types:

  • string

    php
    // You can send requests that use a string as the message body.
    $client->request('PUT', '/put', ['body' => 'foo']);
    
  • resource returned from fopen()

    php
    // You can send requests that use a stream resource as the body.
    $resource = \GuzzleHttp\Psr7\Utils::tryFopen('http://httpbin.org', 'r');
    $client->request('PUT', '/put', ['body' => $resource]);
    
  • Psr\Http\Message\StreamInterface

    php
    // You can send requests that use a Guzzle stream object as the body
    $stream = GuzzleHttp\Psr7\Utils::streamFor('contents...');
    $client->request('POST', '/post', ['body' => $stream]);
    

Resource and object values with __toString() are converted to PSR-7 streams using the configured stream_factory. Callable and iterator bodies use Guzzle's existing PSR-7 stream handling because PSR-17 does not define factories for those stream types. Callable bodies may be closures or invokable objects. Strings are always used as literal body contents, even when they name a callable. Callable arrays are arrays, and arrays are not valid body values. int, float, and bool are not valid body values in Guzzle 8. Request bodies that already implement Psr\Http\Message\StreamInterface are used as provided.

int, float, bool, arrays, and generic objects without __toString() are not valid body values in Guzzle 8.

[!NOTE] This option cannot be used with form_params, multipart, or json

cert

Summary Set to a string to specify the path to a file containing a client side certificate. PEM is the default certificate format. If a password is required, then set to an array containing the path to the certificate file in the first array element followed by the password required for the certificate in the second array element. A null password is treated the same as omitting it. Use cert_type to specify another supported certificate format.

Types

  • string
  • array

Default None

Constant GuzzleHttp\RequestOptions::CERT

php
$client->request('GET', '/', ['cert' => ['/path/server.pem', 'password']]);

[!NOTE] TLS client certificate options remain active during redirects. See Cross-Origin Redirects for details.

cert_type

Summary Specify the SSL client certificate file type.

Types

  • string

Default PEM

Constant GuzzleHttp\RequestOptions::CERT_TYPE

php
$client->request('GET', '/', [
    'cert' => '/path/client.p12',
    'cert_type' => 'P12',
]);

The cURL handler passes this value to CURLOPT_SSLCERTTYPE. Supported values depend on libcurl and its TLS backend.

[!NOTE] The stream handler supports only PEM certificate files.

cookies

Summary Specifies whether or not cookies are used in a request or what cookie jar to use or what cookies to send.

Types

  • GuzzleHttp\Cookie\CookieJarInterface
  • false

Default None

Constant GuzzleHttp\RequestOptions::COOKIES

You must specify the cookies option as a GuzzleHttp\Cookie\CookieJarInterface or false.

true is only a client-constructor shorthand. Guzzle converts ['cookies' => true] in the constructor into a shared GuzzleHttp\Cookie\CookieJar. Per-request cookies values must be false or a CookieJarInterface instance.

php
$jar = new \GuzzleHttp\Cookie\CookieJar();
$client->request('GET', '/get', ['cookies' => $jar]);

[!WARNING] This option only has an effect if your handler has the GuzzleHttp\Middleware::cookies middleware. This middleware is added by default when a client is created with no handler, and is added by default when creating a handler with GuzzleHttp\HandlerStack::create.

[!TIP] When creating a client, you can set the default cookie option to true to use a shared cookie session associated with the client.

connect_timeout

Summary Number of seconds to wait while trying to connect to a server. Use 0 to disable the connect timeout. Positive values below 0.001 seconds are rejected by the built-in cURL handler.

Types

  • int
  • float

Default 60

Constant GuzzleHttp\RequestOptions::CONNECT_TIMEOUT

php
// Timeout if the client fails to connect to the server in 3.14 seconds.
$client->request('GET', '/delay/5', ['connect_timeout' => 3.14]);

[!NOTE] connect_timeout is implemented by cURL handlers. The PHP stream handler does not provide a separate connection-timeout control; it accepts this option without effect so shared request configuration can enable a cURL connection timeout when cURL is available. The stream handler's connect phase is bounded by the read_timeout idle timeout, and by timeout when that is lower.

See Timeout phases for a summary of the timeout options across the built-in handlers.

crypto_method

Summary A value describing the minimum TLS protocol version to use.

Types int

Default TLS 1.2 or newer for HTTPS requests sent by the built-in cURL and stream handlers.

Constant GuzzleHttp\RequestOptions::CRYPTO_METHOD

php
$client->request('GET', '/foo', ['crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT]);

[!NOTE] This setting must be set to one of the STREAM_CRYPTO_METHOD_TLS*_CLIENT constants. It controls the minimum TLS protocol version. cURL 7.52.0 or higher is required to use TLS 1.3 with the cURL handler.

crypto_method_max

Summary A value describing the maximum TLS protocol version to use.

Types int

Default None. HTTPS requests still default to TLS 1.2 or newer through crypto_method.

Constant GuzzleHttp\RequestOptions::CRYPTO_METHOD_MAX

php
$client->request('GET', '/foo', [
    'crypto_method_max' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
]);

[!NOTE] This setting must be set to one of the STREAM_CRYPTO_METHOD_TLS*_CLIENT constants. It may be combined with crypto_method to set an allowed TLS version range. The maximum version must be greater than or equal to the effective minimum version. For HTTPS requests, the built-in handlers default the minimum to TLS 1.2 or newer unless crypto_method is explicitly set. cURL 7.54.0 or higher is required to use maximum TLS-version bounds with the cURL handler.

curl

Summary Raw cURL options to apply when using a built-in cURL handler.

Types

  • array

Default None

Constant GuzzleHttp\RequestOptions::CURL

Except for Guzzle's special body_as_string key, the array is keyed by integer cURL option constants and values are passed to cURL after Guzzle applies request options. Raw cURL options that conflict with Guzzle-managed request handling are rejected by the built-in cURL handlers.

Raw cURL options outside the built-in cURL handlers' allow-list are rejected. Allow-listing means Guzzle passes the option through; PHP, libcurl, or the TLS backend may still reject or ignore an option depending on the runtime. The allow-list is limited to the following CURLOPT_* constants when they are defined by the installed PHP cURL extension:

  • CURLOPT_ADDRESS_SCOPE
  • CURLOPT_CERTINFO
  • CURLOPT_CONNECT_TO
  • CURLOPT_DNS_CACHE_TIMEOUT
  • CURLOPT_DNS_INTERFACE
  • CURLOPT_DNS_LOCAL_IP4
  • CURLOPT_DNS_LOCAL_IP6
  • CURLOPT_DNS_SERVERS
  • CURLOPT_DNS_SHUFFLE_ADDRESSES
  • CURLOPT_ENCODING
  • CURLOPT_FORBID_REUSE
  • CURLOPT_FRESH_CONNECT
  • CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS
  • CURLOPT_HTTPAUTH
  • CURLOPT_HTTPPROXYTUNNEL
  • CURLOPT_INTERFACE
  • CURLOPT_LOCALPORT
  • CURLOPT_LOCALPORTRANGE
  • CURLOPT_LOW_SPEED_LIMIT
  • CURLOPT_LOW_SPEED_TIME
  • CURLOPT_MAXAGE_CONN
  • CURLOPT_MAXCONNECTS
  • CURLOPT_MAXLIFETIME_CONN
  • CURLOPT_PREREQFUNCTION
  • CURLOPT_PROXYHEADER
  • CURLOPT_PROXYUSERPWD
  • CURLOPT_RESOLVE
  • CURLOPT_SSL_CIPHER_LIST
  • CURLOPT_SSL_EC_CURVES
  • CURLOPT_TCP_FASTOPEN
  • CURLOPT_TCP_KEEPALIVE
  • CURLOPT_TCP_KEEPIDLE
  • CURLOPT_TCP_KEEPINTVL
  • CURLOPT_TCP_KEEPCNT
  • CURLOPT_TCP_NODELAY
  • CURLOPT_TLS13_CIPHERS
  • CURLOPT_UNIX_SOCKET_PATH
  • CURLOPT_USERPWD
php
$client->request('GET', '/', [
    'curl' => [
        CURLOPT_FRESH_CONNECT => true,
    ],
]);

CURLOPT_HEADEROPT is intentionally absent from the allow-list above. Guzzle sets it internally to CURLHEADER_SEPARATE whenever it configures CURLOPT_PROXYHEADER, and also for an HTTP(S) proxy CONNECT tunnel even when no proxy header is configured, so proxy headers stay separate from origin request headers; passing CURLOPT_HEADEROPT yourself is rejected.

The built-in cURL handlers treat the first-class Proxy-Authorization request header as proxy-scoped rather than origin-scoped. Managed values never enter the cURL origin header list; they are configured in cURL's proxy-header channel (CURLOPT_PROXYHEADER) regardless of Guzzle's route prediction, and libcurl uses that list only for HTTP requests it actually sends to a proxy. Direct, no-proxy-bypassed, and SOCKS transfers therefore do not send the field to the origin server. An empty value uses cURL's Proxy-Authorization; form in the proxy-only list: it carries no credential or connection signature, but keeps its header-control meaning and suppresses proxy authorization generated from proxy URL userinfo. A non-empty credential is not bound to one proxy identity: it is sent to whichever proxy libcurl selects for the request. Raw CURLOPT_PROXYHEADER remains allow-listed and caller-controlled, and is always paired with header separation.

In Guzzle 8, every first-class field requires libcurl 7.37.0 or newer built with proxy header separation support (with the CURLOPT_PROXYHEADER, CURLOPT_HEADEROPT, and CURLHEADER_SEPARATE constants available). On older libcurl the request is rejected up front with a RequestException regardless of the predicted route. Guzzle 7.14.2 and newer instead omit first-class values on known direct, bypassed, and SOCKS routes, requiring separation only when the final configuration may use an HTTP or HTTPS proxy. Guzzle 8 also rejects a raw CURLOPT_PROXYHEADER list there. Prefer proxy URI userinfo for portable Basic proxy authentication, or the native cURL proxy credential options for cURL-specific authentication.

A proxy CONNECT tunnel carrying a non-empty Proxy-Authorization credential requires a fresh connection, because libcurl cannot key connection reuse on that opaque header value. Under TransportSharing::PERSISTENT_REQUIRE, which requires reuse, such a request is rejected with an InvalidArgumentException instead of silently degrading reuse.

The stream handler has no proxy-only header channel: it omits the first-class field on direct and bypassed routes and rejects a request carrying the field, including an empty value, with an InvalidArgumentException when a proxy is selected. Proxy URI userinfo remains available for Basic proxy authentication on every handler.

debug

Summary Set to true or set to a PHP stream returned by fopen() to enable debug output with the handler used to send a request. For example, when using cURL to transfer requests, cURL's verbose of CURLOPT_VERBOSE will be emitted. When using the PHP stream wrapper, stream wrapper notifications will be emitted. If set to true, the output is written to PHP's STDOUT. If a PHP stream is provided, output is written to the stream.

Types

  • bool
  • fopen() resource

Default None

Constant GuzzleHttp\RequestOptions::DEBUG

php
$client->request('GET', '/get', ['debug' => true]);

Running the above example would output something like the following:

* About to connect() to httpbin.org port 80 (#0)
*   Trying 107.21.213.98... * Connected to httpbin.org (107.21.213.98) port 80 (#0)
> GET /get HTTP/1.1
Host: httpbin.org
User-Agent: GuzzleHttp/8

< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Content-Type: application/json
< Date: Sun, 16 Feb 2014 06:50:09 GMT
< Server: gunicorn/0.17.4
< Content-Length: 335
< Connection: keep-alive
<
* Connection #0 to host httpbin.org left intact

decode_content

Summary Specify whether or not Content-Encoding responses (gzip, deflate, etc.) are automatically decoded.

Types

  • string
  • bool

Default true

Constant GuzzleHttp\RequestOptions::DECODE_CONTENT

This option can be used to control how content-encoded response bodies are handled. By default, decode_content is set to true, meaning any gzipped or deflated response will be decoded by Guzzle.

When set to false, the body of a response is never decoded, meaning the bytes pass through the handler unchanged.

php
// Request gzipped data, but do not decode it while downloading
$client->request('GET', '/foo.js', [
    'headers'        => ['Accept-Encoding' => 'gzip'],
    'decode_content' => false
]);

When set to a string, the bytes of a response are decoded and the string value provided to the decode_content option is passed as the Accept-Encoding header of the request.

php
// Pass "gzip" as the Accept-Encoding header.
$client->request('GET', '/foo.js', ['decode_content' => 'gzip']);

[!WARNING] The Accept-Encoding header will not be sent unless you provide it explicitly, or pass a string value to decode_content. That is equivalent to sending Accept-Encoding: *. Most servers will probably return an uncompressed body in response to that but some might opt to use a compression method that is not supported by your system.

In order to enable compression, and to ensure that only supported encoding methods will be used, you should let curl send the Accept-Encoding header:

php
// Delegate choosing compression method to curl
$client->request('GET', '/foo.js', [
    'curl' => [
        \CURLOPT_ENCODING => '',
    ],
]);

delay

Summary The number of milliseconds to delay before sending the request.

Types

  • integer
  • float

Default null

Constant GuzzleHttp\RequestOptions::DELAY

delay must be an int or float, must be finite, and must be greater than or equal to 0. Negative values, NAN, and INF are rejected before the handler is invoked.

expect

Summary Controls the behavior of the "Expect: 100-Continue" header.

Types

  • bool
  • integer

Default 1048576

Constant GuzzleHttp\RequestOptions::EXPECT

Set to true to enable the "Expect: 100-Continue" header for all requests that sends a body. Set to false to disable the "Expect: 100-Continue" header for all requests. Set to a number so that the size of the payload must be greater than the number in order to send the Expect header. Setting to a number will send the Expect header for all requests in which the size of the payload cannot be determined or where the body is not rewindable.

By default, Guzzle will add the "Expect: 100-Continue" header when the size of the body of a request is greater than 1 MB and a request is using HTTP/1.1.

[!NOTE] This option only takes effect when using HTTP/1.1. The HTTP/1.0, HTTP/2, and HTTP/3 protocols do not support the "Expect: 100-Continue" header. Support for handling the "Expect: 100-Continue" workflow must be implemented by Guzzle HTTP handlers used by a client.

force_ip_resolve

Summary Set to "v4" if you want the HTTP handlers to use only ipv4 protocol or "v6" for ipv6 protocol.

Types "v4" or "v6"

Default null

Constant GuzzleHttp\RequestOptions::FORCE_IP_RESOLVE

php
// Force ipv4 protocol
$client->request('GET', '/foo', ['force_ip_resolve' => 'v4']);

// Force ipv6 protocol
$client->request('GET', '/foo', ['force_ip_resolve' => 'v6']);

Only the exact, case-sensitive values v4 and v6 are accepted.

[!NOTE] This setting must be supported by the HTTP handler used to send a request. force_ip_resolve is currently only supported by the built-in cURL and stream handlers.

form_params

Summary Used to send an application/x-www-form-urlencoded POST request.

Types array

Constant GuzzleHttp\RequestOptions::FORM_PARAMS

Array mapping form field names to scalar, null, or nested array values. Values are serialized with PHP's http_build_query(). Sets the Content-Type header to application/x-www-form-urlencoded when no Content-Type header is already present.

php
$client->request('POST', '/post', [
    'form_params' => [
        'foo' => 'bar',
        'baz' => ['hi', 'there!']
    ]
]);

[!NOTE] form_params cannot be used with the multipart option. You will need to use one or the other. Use form_params for application/x-www-form-urlencoded requests, and multipart for multipart/form-data requests.

This option cannot be used with body, multipart, or json

headers

Summary Array keyed by header names to add to the request. List-style header arrays are rejected. PHP stores numeric-string header names as integer keys; when such keys are accepted, Guzzle casts header keys back to strings while applying them. Each value is a string or non-empty array of strings representing the header field values.

Types

  • array
  • null

Defaults None

Constant GuzzleHttp\RequestOptions::HEADERS

php
// Set various headers on a request
$client->request('GET', '/get', [
    'headers' => [
        'User-Agent' => 'testing/1.0',
        'Accept'     => 'application/json',
        'X-Foo'      => ['Bar', 'Baz']
    ]
]);

Headers may be added as default options when creating a client. When headers are used as default options, they are only applied if the request being created does not already contain the specific header. This includes both requests passed to the client in the send() and sendAsync() methods, and requests created by the client (e.g., request() and requestAsync()).

php
$client = new GuzzleHttp\Client(['headers' => ['X-Foo' => 'Bar']]);

// Will send a request with the X-Foo header.
$client->request('GET', '/get');

// Sets the X-Foo header to "test", which prevents the default header
// from being applied.
$client->request('GET', '/get', ['headers' => ['X-Foo' => 'test']]);

// Will disable adding in default headers.
$client->request('GET', '/get', ['headers' => null]);

// Will not overwrite the X-Foo header because it is in the message.
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'http://foo.com', ['X-Foo' => 'test']);
$client->send($request);

// Will overwrite the X-Foo header with the request option provided in the
// send method.
use GuzzleHttp\Psr7\Request;
$request = new Request('GET', 'http://foo.com', ['X-Foo' => 'test']);
$client->send($request, ['headers' => ['X-Foo' => 'overwrite']]);

http_errors

Summary Set to false to disable throwing exceptions on an HTTP protocol errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when HTTP protocol errors are encountered.

Types bool

Default true

Constant GuzzleHttp\RequestOptions::HTTP_ERRORS

php
$client->request('GET', '/status/500');
// Throws a GuzzleHttp\Exception\ServerException

$res = $client->request('GET', '/status/500', ['http_errors' => false]);
echo $res->getStatusCode();
// 500

[!WARNING] This option only has an effect if your handler has the GuzzleHttp\Middleware::httpErrors middleware. This middleware is added by default when a client is created with no handler, and is added by default when creating a handler with GuzzleHttp\HandlerStack::create.

idn_conversion

Summary Internationalized Domain Name (IDN) support.

Types

  • bool
  • int
  • null

Default false

Constant GuzzleHttp\RequestOptions::IDN_CONVERSION

php
$client->request('GET', 'https://яндекс.рф');
// яндекс.рф is translated to xn--d1acpjx3f.xn--p1ai before passing it to the handler

$res = $client->request('GET', 'https://яндекс.рф', ['idn_conversion' => false]);
// The domain part (яндекс.рф) stays unmodified

Enables/disables IDN support, can also be used for precise control by combining IDNA_* constants (except IDNA_ERROR_*), see the $options parameter in the idn_to_ascii() documentation for more details. Pass false or null to disable IDN conversion.

json

Summary The json option is used to easily upload JSON encoded data as the body of a request. A Content-Type header of application/json will be added if no Content-Type header is already present on the message. An Accept header is not added automatically; pass one explicitly if the server requires JSON response content negotiation.

Types Any PHP type that can be operated on by PHP's json_encode() function.

Default None

Constant GuzzleHttp\RequestOptions::JSON

php
$response = $client->request('PUT', '/put', ['json' => ['foo' => 'bar']]);

[!NOTE] This request option does not support customizing the Content-Type header or any of the options from PHP's json_encode() function. If you need to customize these settings, then you must pass the JSON encoded data into the request yourself using the body request option and you must specify the correct Content-Type header using the headers request option.

This option cannot be used with body, form_params, or multipart

multipart

Summary Sets the body of the request to a multipart/form-data form.

Types array

Constant GuzzleHttp\RequestOptions::MULTIPART

The value of multipart is an array of part arrays, each containing the following key value pairs:

  • name: (string|int, required) the form field name
  • contents: (mixed, required) Any non-array value accepted by GuzzleHttp\Psr7\Utils::streamFor(), including strings, resources, streams, iterators, closures, and invokable objects. Arrays are expanded as nested multipart fields; headers and filename cannot be used when contents is an array.
  • headers: (array) Optional array of custom string header values to use with the form element.
  • filename: (string) Optional string to send as the filename in the part.
php
use GuzzleHttp\Psr7;

$client->request('POST', '/post', [
    'multipart' => [
        [
            'name'     => 'foo',
            'contents' => 'data',
            'headers'  => ['X-Baz' => 'bar']
        ],
        [
            'name'     => 'baz',
            'contents' => Psr7\Utils::tryFopen('/path/to/file', 'r')
        ],
        [
            'name'     => 'qux',
            'contents' => Psr7\Utils::tryFopen('/path/to/file', 'r'),
            'filename' => 'custom_filename.txt'
        ],
    ]
]);

[!NOTE] multipart cannot be used with the form_params option. You will need to use one or the other. Use form_params for application/x-www-form-urlencoded requests, and multipart for multipart/form-data requests.

This option cannot be used with body, form_params, or json

multiplex

Summary Controls how a request sent through a built-in cURL handler relates to shared, multiplexed connections: how an HTTP/2 or HTTP/3 request pursues one, or, with Multiplexing::NONE, whether the transfer may share its connection at all.

Types

  • string (one of the GuzzleHttp\Multiplexing mode constants)

Default Multiplexing::WAIT

Constant GuzzleHttp\RequestOptions::MULTIPLEX

libcurl multiplexes concurrent HTTP/2 and HTTP/3 transfers over a single connection whenever a multiplexable connection to the origin already exists, whatever this option is set to. The modes grade how much further the request goes:

  • Multiplexing::EAGER - never wait for a connection that is still being established: a burst of requests against a cold origin opens parallel connections.
  • Multiplexing::WAIT (default) - wait for a pending connection that libcurl considers eligible for multiplexing, normally one to the same origin, and share it. Silently ignored by the stream handler and the blocking CurlHandler, which has no multi handle to multiplex over. If the connection turns out not to multiplex, waiting requests open their own.
  • Multiplexing::REQUIRE_EAGER - guarantee a multiplexed protocol or fail loudly, while dialing eagerly. HTTP/2 requests are sent with prior knowledge, so TLS connections offer only h2 via ALPN (libcurl 8.14.0+) and cleartext connections speak HTTP/2 directly; cleartext requests sent through a proxy are rejected. HTTP/3 requests are pinned to HTTP/3 with no downgrade at all (libcurl 8.13.0+, PHP 8.4+); a proxy cannot carry them and is rejected. A server limited to lower protocol versions fails the connection instead of downgrading. The required family also rejects final CURLOPT_HTTPAUTH masks that permit NTLM, which libcurl retries over HTTP/1.1. Requires protocol version 2/2.0 or 3/3.0 and a cURL handler; anything else throws. A cold burst dials connections in parallel, but libcurl still packs later streams onto the first established connection rather than balancing.
  • Multiplexing::REQUIRE_WAIT - the same guarantees as Multiplexing::REQUIRE_EAGER, plus WAIT's waiting on pending connections. The required modes require a handler that permits actual multiplexing, not merely a multiplexed protocol, and are rejected on a Multiplexing::NONE handler.

Multiplexing::NONE disables multiplexing for a whole handler when passed as the multiplex client configuration option, which configures the default handler and also becomes the default request option, or, when constructing a handler directly, as the CurlMultiHandler multiplex constructor option. A handler configured with Multiplexing::NONE wins over the default WAIT: default requests run without waiting, explicitly requested wait modes are rejected as a configuration conflict when the transfer would actually wait, and the required modes are always rejected, because they require a handler that permits actual multiplexing, not merely a multiplexed protocol. As a request option value, Multiplexing::NONE guarantees the transfer does not share its connection with any concurrent transfer. Multiplexing::NONE does not force HTTP/1.1: on a Multiplexing::NONE handler, HTTP/2 still negotiates and each transfer keeps its connection to itself.

The request option value is accepted exactly where the guarantee holds and can be verified: on a CurlMultiHandler configured with Multiplexing::NONE, for requests whose declared protocol version is HTTP/1.x, on CurlHandler, and on the stream handler, which never multiplexes. An HTTP/2 or HTTP/3 request with a Multiplexing::NONE request option is rejected on a CurlMultiHandler that permits multiplexing. Acceptance is decided from the request's declared protocol version, before any transport-level downgrade: an HTTP/3 request sent through a proxy is delivered over HTTP/2 or HTTP/1.1 on the wire, but is still rejected. On a CurlMultiHandler that permits multiplexing, Multiplexing::NONE is also rejected with a custom handle_factory, when the handler requires persistent transport sharing (the safeguards can require a fresh connection, which required sharing forbids), when the request carries an Expect: 100-continue header (its 417 retries select connections outside the safeguards; remove an explicitly supplied header, or set the expect request option to false to prevent it being added automatically), and combined with a raw CURLOPT_HTTPAUTH cURL option, whose authentication retries do the same.

On a client whose multi handler permits multiplexing, the ordinary non-streaming default stack - both cURL handlers available and no connection caps forcing multi-only routing - runs synchronous requests on the CurlHandler path, which satisfies the guarantee for any protocol version, while asynchronous requests run on the CurlMultiHandler, so an HTTP/2 request with Multiplexing::NONE succeeds synchronously and is rejected asynchronously on the same client. Keep-alive reuse between consecutive transfers is unaffected, except on libcurl versions below 7.77.0 and from 8.11.0 through 8.12.1, where an accepted HTTP/1.x request on a multiplexing CurlMultiHandler forces a fresh connection. Custom handlers receive the multiplex option unchanged: its semantics are handler-defined, Guzzle does not guarantee it is honored, and a client-level Multiplexing::NONE with a custom handler flows to it as a default request option without client-side enforcement.

php
$client->requestAsync('GET', 'https://example.com/big-file', [
    'version' => '2.0',
    'multiplex' => Multiplexing::EAGER,
]);

[!NOTE] None of the modes is a connection cap: once an established HTTP/2 connection has no free streams - servers commonly allow about 100 - additional requests open additional connections regardless of this option.

[!NOTE] libcurl never reuses or coalesces a connection across differing TLS settings (verify, custom CA, client certificate/key, pinned public key) or proxy settings, so a verified request can never ride an unverified connection. Because libcurl coalesces HTTP/2 connections, requests to different hostnames that resolve to the same address and are covered by the server certificate may share one connection; a server not authoritative for the second name can reject it with HTTP/2 421 Misdirected Request. Waiting requests share one in-progress connection, so a slow lead connection adds latency to, and is charged against the timeout of, the requests waiting on it. Only requests whose protocol version resolves to HTTP/2 or HTTP/3 wait. Use Multiplexing::EAGER when you rely on independent connection timing; it stops the waiting but does not guarantee separate connections; established multiplex-capable connections are still shared.

on_headers

Summary A callable that is invoked when the HTTP headers of the final response, or a 101 Switching Protocols response, have been received but the body has not yet begun to download.

Types

  • callable

Constant GuzzleHttp\RequestOptions::ON_HEADERS

The callable accepts a Psr\Http\Message\ResponseInterface object and the corresponding Psr\Http\Message\RequestInterface object. Built-in handlers do not invoke it for interim informational responses such as 100 Continue or 103 Early Hints. If an exception is thrown by the callable, then the promise associated with the response will be rejected with a GuzzleHttp\Exception\ResponseException that wraps the exception that was thrown.

You may need to know what headers and status codes were received before data can be written to the sink.

php
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

// Reject responses that are greater than 1024 bytes.
$client->request('GET', 'http://httpbin.org/stream/1024', [
    'on_headers' => function (ResponseInterface $response, RequestInterface $request) {
        if ($response->getHeaderLine('Content-Length') > 1024) {
            throw new \Exception('The file is too big!');
        }
    }
]);

When requests are sent with GuzzleHttp\Pool and this callback is supplied via the pool's options configuration, the callable also receives the iterable key that identified the request as a third argument:

php
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

$requests = [
    'image' => new Request('GET', 'http://httpbin.org/image/jpeg'),
    'avatar' => new Request('GET', 'http://httpbin.org/image/png'),
];

$pool = new Pool($client, $requests, [
    'options' => [
        'on_headers' => function (ResponseInterface $response, RequestInterface $request, $key) {
            // $key is 'image' or 'avatar'
        },
    ],
]);

$pool->promise()->wait();

[!NOTE] When writing HTTP handlers, the on_headers function must be invoked for the final response, or for a 101 Switching Protocols response, before writing data to the body of the response.

on_stats

Summary on_stats allows you to get access to transfer statistics of a request and access the lower level transfer details of the handler associated with your client. on_stats is a callable that is invoked when a handler has finished sending a request. The callback is invoked with transfer statistics about the request, the response received, or the error encountered. Included in the data is the total amount of time taken to send the request.

Types

  • callable

Constant GuzzleHttp\RequestOptions::ON_STATS

The callable accepts a GuzzleHttp\TransferStats object. Built-in handlers reject non-callable on_stats values before starting the transfer.

Exceptions thrown by on_stats are not wrapped by Guzzle and may escape from the handler wait path. With the built-in cURL handlers, native cURL handles are released before on_stats is invoked. cURL handlers emit on_stats per low-level transfer attempt, so retries may invoke it more than once for one logical request.

php
use GuzzleHttp\TransferStats;

$client = new GuzzleHttp\Client();

$client->request('GET', 'http://httpbin.org/stream/1024', [
    'on_stats' => function (TransferStats $stats) {
        echo $stats->getEffectiveUri() . "\n";
        echo $stats->getTransferTime() . "\n";
        var_dump($stats->getHandlerStats());

        // You must check if a response was received before using the
        // response object.
        if ($stats->hasResponse()) {
            echo $stats->getResponse()->getStatusCode();
        } else {
            // Error data is handler specific. You will need to know what
            // type of error data your handler uses before using this
            // value.
            var_dump($stats->getHandlerErrorData());
        }
    }
]);

When requests are sent with GuzzleHttp\Pool and this callback is supplied via the pool's options configuration, the callable also receives the iterable key that identified the request as a second argument.

on_trailers

Summary A callable that is invoked exactly once when a transfer completes successfully, with the HTTP trailer fields of the response.

Types

  • callable

Constant GuzzleHttp\RequestOptions::ON_TRAILERS

The callable accepts an associative array of trailer field names mapped to lists of field values, the Psr\Http\Message\ResponseInterface object, and the corresponding Psr\Http\Message\RequestInterface object. The built-in cURL handlers invoke it after the entire response body has been written to the sink, after on_headers, and before on_stats. The array is empty when the response carried no trailer fields, for example because the server sent none or sent them as ordinary headers. Trailer field names are lowercased and grouped case-insensitively, matching the HTTP/2 wire format; values keep their wire order. The callable is never invoked for failed transfers. If an exception is thrown by the callable, then the promise associated with the response will be rejected with a GuzzleHttp\Exception\ResponseException that wraps the exception that was thrown.

php
use GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

// Verify a content checksum delivered after the body.
$client->request('GET', 'https://example.com/stream', [
    'version' => '2.0',
    'on_trailers' => function (array $trailers, ResponseInterface $response, RequestInterface $request) {
        $checksum = $trailers['x-checksum'][0] ?? null;
        if ($checksum === null || !hash_equals($checksum, Psr7\Utils::hash($response->getBody(), 'sha256'))) {
            throw new \Exception('Response body checksum missing or mismatched!');
        }
    }
]);

When requests are sent with GuzzleHttp\Pool and this callback is supplied via the pool's options configuration, the callable also receives the iterable key that identified the request as a fourth argument.

[!NOTE] Only the built-in cURL handlers invoke on_trailers; the built-in stream handler rejects the option because it cannot observe trailer fields, and the mock handler ignores it. When writing HTTP handlers that support trailer fields, invoke the on_trailers callable exactly once per successful transfer, after the response body has completed, and never for failed transfers. Malformed trailer field lines are discarded before parsing. Trailer fields are reported separately from response headers and are never merged into the response.

progress

Summary Defines a function to invoke when transfer progress is made.

Types

  • callable

Default None

Constant GuzzleHttp\RequestOptions::PROGRESS

The function accepts the following positional arguments:

  • the total number of bytes expected to be downloaded, zero if unknown
  • the number of bytes downloaded so far
  • the total number of bytes expected to be uploaded
  • the number of bytes uploaded so far

With the built-in cURL handlers, returning a truthy value aborts the transfer and rejects the request promise with a GuzzleHttp\Exception\ResponseException when a response is available, or a GuzzleHttp\Exception\RequestException otherwise. If the callable throws, the built-in cURL handlers abort the transfer and reject the promise with the same response-aware classification while wrapping the thrown exception. If a built-in handler receives a progress byte count that cannot be represented as a PHP integer, the transfer is rejected before the callback is invoked. The built-in stream handler treats progress callbacks as notifications only and ignores return values.

php
// Send a GET request to /get?foo=bar
$result = $client->request(
    'GET',
    '/',
    [
        'progress' => function(
            int $downloadTotal,
            int $downloadedBytes,
            int $uploadTotal,
            int $uploadedBytes
        ) {
            //do something
        },
    ]
);

When requests are sent with GuzzleHttp\Pool and this callback is supplied via the pool's options configuration, the callable also receives the iterable key that identified the request as a fifth argument.

protocols

Summary Allowed URI schemes for request transfers.

Types

  • non-empty array

Default ['http', 'https']

Constant GuzzleHttp\RequestOptions::PROTOCOLS

This option accepts a non-empty array containing only the case-sensitive values http and/or https. It applies to each request transfer Guzzle sends, including redirect requests that reuse the same request options.

php
$client->request('GET', 'https://example.com', [
    'protocols' => ['https'],
]);

[!NOTE] protocols replaces raw cURL CURLOPT_PROTOCOLS when restricting request schemes. Raw cURL options that conflict with Guzzle-managed request handling are rejected. Use Guzzle request options instead when configuring the request method, URI, body, headers, timeouts, redirects, proxy, TLS, progress, debug output, sinks, cookies, and protocols. Redirect middleware also validates redirect targets with allow_redirects.protocols before creating each redirect request.

proxy

Summary Pass a string to specify a proxy, or an array to specify different proxies for different protocols.

Types

  • string
  • array

Default None

Constant GuzzleHttp\RequestOptions::PROXY

Pass a string to specify a proxy for all protocols.

php
$client->request('GET', '/', ['proxy' => 'http://localhost:8125']);

Pass an associative array to specify proxies for specific URI schemes (i.e., "http", "https"). Provide a no key value pair as a comma- or whitespace-delimited string or an array of entries that should not be proxied to; array entries are taken as-is and are never re-split. The http, https, and no entries may be set to null to leave that entry unconfigured.

The no list supports the following entry forms:

no entryBypasses the proxy for
*every request
*:80any host on effective port 80
example.com, .example.comexample.com and its subdomains
example.com:8080example.com and its subdomains on port 8080
127.0.0.1, ::1, [::1]requests whose host is that IP literal
[::1]:8080that IP literal on port 8080
10.0.0.0/8, fd00::/8IP-literal hosts inside the range

Domain entries are matched case-insensitively, and one final DNS root dot is ignored on each side before matching; repeated trailing dots are not collapsed, and only a single leading dot is ignored — entries with repeated leading dots match nothing. IP literals are normalized before matching, so equivalent IPv6 spellings such as ::1 and 0:0:0:0:0:0:0:1 match; trailing-dot normalization does not apply to IP literals or CIDR rules. IP and CIDR entries match only requests whose host is itself an IP literal: host names are never resolved to addresses when deciding whether to proxy. Ports are matched against the request's effective port — an explicit port in the URI, otherwise the scheme default (80 for "http", 443 for "https") — and CIDR entries are not port-specific.

[!NOTE] Guzzle will automatically populate this value with your environment's NO_PROXY environment variable. However, when providing a proxy request option, it is up to you to provide the no value from the NO_PROXY environment variable.

Custom handlers can use GuzzleHttp\ProxyOptions::resolve() to apply Guzzle-compatible proxy selection. The helper resolves the documented proxy request option shape, including scheme-specific proxy entries and no exclusion rules. Handlers remain responsible for translating the selected proxy string into their transport-specific configuration. The environment-variable fallback performed by the built-in handlers is not part of this helper; custom handlers that want it must implement their own environment lookup.

php
use GuzzleHttp\ProxyOptions;

$selection = ProxyOptions::resolve($request->getUri(), $options['proxy'] ?? null);

if ($selection->hasProxy()) {
    $proxy = $selection->getProxy();
    // Configure the transport to use $proxy.
} elseif ($selection->shouldDisableProxy()) {
    // Disable transport-level default or environment proxy behavior.
}
php
$client->request('GET', '/', [
    'proxy' => [
        'http'  => 'http://localhost:8125', // Use this proxy with "http"
        'https' => 'http://localhost:9124', // Use this proxy with "https",
        'no' => ['.mit.edu', 'foo.com', 'example.com:8080', '::1', '[fd00::1]:8080', '10.0.0.0/8', 'fd00::/8'] // Don't use a proxy with these
    ]
]);

[!NOTE] You can provide proxy URLs that contain a scheme, username, and password. For example, "http://username:[email protected]:10". A scheme-less value such as "127.0.0.1:8125" is treated as an HTTP proxy. Both built-in handlers validate the proxy URL up front and reject a malformed one (an invalid host, an out-of-range port, or leading junk before the scheme) with an InvalidArgumentException.

Handler support

The proxy option — including the no list and its validation — means the same thing on every built-in handler: which proxy, if any, applies to a request is decided by the same rules everywhere. What differs is the transport executing that decision:

CapabilitycURL handlersStream handler
HTTP proxy for "http" requestsyesyes
HTTP proxy for "https" requests (CONNECT tunnel)yes (libcurl 7.54+)no
HTTPS proxy (TLS to the proxy itself)yes (libcurl 7.54+)no
SOCKS proxies (socks4://, socks4a://, socks5://, socks5h://)yesno
Proxy credentials in the proxy URLyesyes (Basic only)
Proxy resolution from environment variablesyesyes

The stream handler forwards requests through PHP's HTTP stream wrapper, which supports plain HTTP proxying only: it cannot establish CONNECT tunnels, so "https" requests through a proxy fail with a connection error. It rejects any proxy URL whose scheme it cannot execute, throwing an InvalidArgumentException before the request is sent. That covers https://, SOCKS (socks4://, socks4a://, socks5://, socks5h://), and anything other than http:// or a raw PHP transport such as tcp://, ssl://, or tls://. A recognized SSL/TLS-family transport that this PHP build's stream_get_transports() does not provide (for example tls:// on a build without OpenSSL) is build-specific, so it throws a RequestException instead of the InvalidArgumentException used for a scheme invalid on every build. The stream handler now resolves proxies from the environment too (see below); the scheme rejections above apply identically to an environment-resolved proxy. A proxy given without an explicit port defaults to port 1080, libcurl's default HTTP proxy port.

HTTPS proxies (an https:// proxy URL, where the connection to the proxy itself is encrypted) require libcurl 7.54.0 or newer built with HTTPS-proxy support. The cURL handlers reject a request using an HTTPS proxy on anything older up front, with a RequestException. They also reject any proxy URL whose scheme libcurl cannot use as a proxy, with an InvalidArgumentException. Proxying an "https" request establishes a CONNECT tunnel through the proxy, which likewise requires libcurl 7.54.0 or newer and is rejected up front on older libcurl (including tunnels forced through raw curl options). The proxy's interim 200 Connection established reply is suppressed on these tunnels, so on_headers observes only origin responses and a tunneled transfer failure is classified by its transport phase.

Proxy environment variables

The built-in handlers resolve proxies from the environment themselves: the cURL handlers configure libcurl's proxy options explicitly so libcurl never reads the environment itself, and the stream handler resolves the same way and installs the result in the PHP stream context. When the proxy request option makes a decision for a request, that decision is final, and proxy environment variables are ignored for the request. For an "https" request, the option resolves like this:

proxy option valueResult
option not set, or nullenvironment proxies apply
'http://proxy.example.com:8080'that proxy is used; environment ignored
''no proxy; environment ignored
['https' => 'http://proxy.example.com:8080']that proxy is used; environment ignored
['https' => '']no proxy; environment ignored
['https' => null], or an array without an https entryenvironment proxies apply
any array whose no list matches the requestdirect connection; environment ignored (takes precedence over the array rows above)

In particular, the no_proxy/NO_PROXY environment variables do not bypass an explicitly configured proxy; add the hosts to the option's no list instead.

When the proxy request option makes no decision for a request, the built-in handlers resolve the proxy from the environment with the same lookup semantics libcurl uses:

  1. The lowercase scheme-specific variable, e.g. https_proxy for an "https" request. For "http" requests, the uppercase HTTP_PROXY variant is never read (see https://httpoxy.org, and the Windows note below); for other schemes the uppercase variant is read when the lowercase one is not set.
  2. all_proxy, then ALL_PROXY.

The first variable with a non-empty value ends the lookup; variables set to an empty string are treated as unset, matching libcurl. When an environment proxy is found, the no_proxy (or NO_PROXY) environment variable is matched against the request by Guzzle. The value is tokenized the way libcurl tokenizes it — entries may be separated by commas or whitespace, and a single leading dot is ignored, so .example.com bypasses example.com and its subdomains — and each entry is then matched using the same rules as the option's no list (including CIDR ranges); a match disables the proxy for the request.

Only the real process environment is consulted, matching libcurl: values injected per-request by the SAPI (e.g. fastcgi_param or SetEnv) are not read by this handler-level resolution. On Windows, environment variable names are case-insensitive, so the lowercase-only protection for HTTP_PROXY is not possible; outside the CLI SAPI on Windows, proxy environment variables are therefore not resolved at all, and the proxy request option must be used instead.

Separately from the handler-level resolution above, a GuzzleHttp\Client maps the uppercase HTTP_PROXY (CLI SAPI only), HTTPS_PROXY, and NO_PROXY environment variables into a default for the proxy request option. The client mapping reads $_SERVER first, so it does honor SAPI-provided values such as those set with fastcgi_param or SetEnv. See Environment Variables.

[!NOTE] When sending HTTPS requests, or requests explicitly tunneled with CURLOPT_HTTPPROXYTUNNEL, through an HTTP or HTTPS proxy, libcurl versions before 8.19.0 could reuse an existing proxy tunnel even after proxy credentials changed, and 8.19.0 still contains related proxy credential leak flaws that were fixed in 8.20.0. Guzzle therefore sections proxy tunnel connection reuse by the proxy credentials in effect: requests with the same credentials reuse a pooled tunnel, while a change of proxy credentials (from the proxy option, the environment, or cURL proxy credential options supplied through the curl request option) is isolated onto its own connections. Anonymous tunnels are sectioned apart from authenticated ones, so an unauthenticated request never rides an authenticated tunnel. Raw CURLOPT_PROXY is rejected; use the proxy request option for the proxy URL. A non-empty custom proxy authentication value sent with CURLOPT_PROXYHEADER is always sectioned because libcurl cannot key connection reuse on those header values. On libcurl 8.20.0 and newer, credential sectioning for option-supplied credentials is left to libcurl's own credential-aware connection matching.

SOCKS proxies authenticate the connection itself rather than a CONNECT tunnel, and libcurl versions before 7.69.0 do not compare SOCKS credentials when matching a pooled connection for reuse, so on those versions Guzzle sections every SOCKS-proxied request by its proxy and credential state — plain http:// targets and credential-less requests included. From libcurl 7.69.0, SOCKS credential matching is left to libcurl.

A configured share handle forces every SOCKS request onto a fresh, non-reusable connection before libcurl 7.69.0 because its provenance is opaque to CurlFactory. This applies to Guzzle-managed transport_sharing handles too, even though they do not share connection caches on those libcurl versions. Caller-supplied false CURLOPT_FRESH_CONNECT and CURLOPT_FORBID_REUSE values cannot disable this rule.

HTTP and HTTPS proxy tunnels have an analogous opaque-share rule. From libcurl 7.57.0 a cURL share handle can own a connection cache, and a tunnel seeded there with a literal Proxy-Authorization header is never keyed on credentials, so an anonymous tunnel through a share whose connection cache may have outside producers is forced onto a fresh, non-reusable connection; under TransportSharing::PERSISTENT_REQUIRE such a tunnel is rejected instead. This covers share handles passed directly to CurlFactory and Guzzle's persistent sharing modes, whose connection cache is worker-global. Handler-lifetime transport_sharing shares never lock connections and keep their current reuse behavior, and requests carrying recognized proxy credential state keep the version-gated safeguards above. Caller-supplied false CURLOPT_FRESH_CONNECT and CURLOPT_FORBID_REUSE values cannot disable this rule either.

Sectioning has a cost in mixed workloads: changing the proxy credentials in use discards the idle pooled connections held for the previous credentials, which also drops unrelated direct keep-alive connections pooled alongside them, and alternating anonymous and authenticated traffic through the same proxy repeats that cost on each switch. Advanced users can still control cURL connection reuse explicitly with the curl request option and CURLOPT_FRESH_CONNECT or CURLOPT_FORBID_REUSE.

query

Summary Array of query string values or query string to add to the request.

Types

  • array
  • string

Default None

Constant GuzzleHttp\RequestOptions::QUERY

php
// Send a GET request to /get?foo=bar
$client->request('GET', '/get', ['query' => ['foo' => 'bar']]);

Query strings specified in the query option will overwrite all query string values supplied in the URI of a request.

php
// Send a GET request to /get?foo=bar
$client->request('GET', '/get?abc=123', ['query' => ['foo' => 'bar']]);

read_timeout

Summary Number of seconds the connection may sit silent at any stage of the request. Use 0 to disable the idle timeout. Positive values below 0.001 seconds are rejected by the built-in stream handler.

Types

  • int
  • float

Default 60

Constant GuzzleHttp\RequestOptions::READ_TIMEOUT

The idle timeout bounds the silence between packets while connecting, while waiting for response headers, and between reads on the response body, whether the body is buffered or streamed; any received data resets it. It is implemented by the PHP stream handler. cURL handlers accept this option without effect so shared request configuration can be reused across transports. With Guzzle's default handler selection, streamed responses (stream => true) use the stream handler when PHP streams are available. When the stream handler buffers a response with timeout set, each body read is bounded by the shorter of the idle timeout and the remaining total timeout.

See Timeout phases for a summary of the timeout options across the built-in handlers.

When a read on the streamed PSR-7 body times out, read() throws GuzzleHttp\Psr7\Exception\TimeoutException. A non-timeout read failure, for example the connection is reset mid-body or the body stream has been detached, throws a plain \RuntimeException. Both types sit outside the GuzzleHttp\Exception\GuzzleException hierarchy: they are raised after the request has resolved and the response object has been returned, which is outside the Client::sendRequest() transfer that PSR-18 governs. Catch them explicitly when reading a streamed body. If you detach the body and read the raw PHP stream resource instead, functions such as fgets() return false on timeout, following PHP's stream semantics.

A timed-out read does not invalidate the streamed body. Catching the TimeoutException and calling read() again resumes waiting for data, so idle timeouts can serve as poll ticks on long-lived streams.

php
$response = $client->request('GET', '/stream', [
    'stream' => true,
    'read_timeout' => 10,
]);

$body = $response->getBody();

// Throws GuzzleHttp\Psr7\Exception\TimeoutException on timeout
$data = $body->read(1024);

// A timed-out read can be retried, so idle timeouts work as poll ticks
try {
    $data = $body->read(1024);
} catch (\GuzzleHttp\Psr7\Exception\TimeoutException $e) {
    // Check for cancellation, then call read() again to resume waiting.
}

// Returns false on timeout (raw stream resource)
$line = fgets($body->detach());

request_factory

Summary PSR-17 request factory used when Guzzle creates requests.

Types Psr\Http\Message\RequestFactoryInterface

Default GuzzleHttp\Psr7\HttpFactory

Constant GuzzleHttp\RequestOptions::REQUEST_FACTORY

php
$factory = new \GuzzleHttp\Psr7\HttpFactory();

$client->request('GET', '/get', [
    'request_factory' => $factory,
]);

This option can be set on a client or per request. It affects request-side object creation only and does not affect response implementations returned by handlers.

[!NOTE] This option only affects requests created by request(), requestAsync(), and shortcut methods such as get() and post(). Requests passed to send(), sendAsync(), or sendRequest() are used as provided.

[!WARNING] Guzzle only checks that the value implements Psr\Http\Message\RequestFactoryInterface; it does not validate the requests it returns. Per PSR-7 the Host header is derived from the request URI, and Guzzle does not recompute it afterwards, so a factory that fails to set Host from the URI — or that lets a caller-controlled Host diverge from the URI actually dialed — can cause host confusion, cache poisoning, or requests routed to an unexpected origin. Supply a request implementation you trust to follow PSR-7.

response_factory

Summary PSR-17 response factory used by the built-in handlers when creating the response message.

Types Psr\Http\Message\ResponseFactoryInterface

Default GuzzleHttp\Psr7\HttpFactory

Constant GuzzleHttp\RequestOptions::RESPONSE_FACTORY

php
$factory = new \GuzzleHttp\Psr7\HttpFactory();

$client->request('GET', '/get', [
    'response_factory' => $factory,
]);

This option can be set on a client or per request. The built-in cURL and stream handlers build the response message (status code, reason phrase, headers, and protocol version) with this factory, and create the response body stream with the configured stream_factory where practical. The factory should return an empty, header-less response, because the handlers apply the parsed status line, headers, and body themselves.

[!NOTE] This option is consumed by the built-in handlers when they create a response. MockHandler returns the responses you queue, and custom handlers are responsible for honoring this option themselves.

[!WARNING] Guzzle only checks that the value implements Psr\Http\Message\ResponseFactoryInterface; it does not validate the responses it returns. The handlers add each parsed header to the returned response with withAddedHeader(), so a factory that pre-seeds headers (or returns a non-empty response) duplicates them — for example emitting two Content-Type or Content-Length values and corrupting the message. The factory should return an empty, header-less response, and must reject CR/LF in any header it sets itself, or it can re-introduce header-injection and response-splitting issues. Supply a response implementation you trust.

retries

Summary Current retry count used by GuzzleHttp\Middleware::retry().

Types int

Default 0 when retry middleware is used

Constant GuzzleHttp\RequestOptions::RETRIES

The retry middleware initializes this option to 0 before the first attempt and increments it before each retry. Applications may seed it on a per-request basis when using the retry middleware.

stream_factory

Summary PSR-17 stream factory used when Guzzle creates request body streams and, for the built-in handlers, response body streams where practical.

Types Psr\Http\Message\StreamFactoryInterface

Default GuzzleHttp\Psr7\HttpFactory

Constant GuzzleHttp\RequestOptions::STREAM_FACTORY

php
$factory = new \GuzzleHttp\Psr7\HttpFactory();

$client->request('POST', '/post', [
    'body' => 'payload',
    'stream_factory' => $factory,
]);

This option can be set on a client or per request. It is used when Guzzle converts supported body, form_params, or json request option values into Psr\Http\Message\StreamInterface instances, when redirect handling resets a request body, and by the built-in cURL and stream handlers when they wrap response body resources where practical. Request bodies that already implement Psr\Http\Message\StreamInterface are used as provided.

[!NOTE] This option does not replace every stream. Callable and iterator request bodies use Guzzle's existing PSR-7 stream handling, multipart internals are left untouched, string path sinks open lazily, and caller-owned resource sinks keep Guzzle's own stream wrapper so write-only sink resources remain supported. MockHandler queued responses and custom handler responses are used as provided. When decoding gzip/deflate responses, the factory still wraps the underlying transport resource, but Guzzle layers its own InflateStream decorator on top because PSR-17 cannot express a decoding stream.

[!WARNING] Guzzle only checks that the value implements Psr\Http\Message\StreamFactoryInterface; it does not validate the streams it returns. For responses, createStreamFromResource() wraps the live transport socket, so the returned stream must behave like the default GuzzleHttp\Psr7\Stream. It must expose the resource's live timed_out metadata: a stream that snapshots metadata, or omits the timed_out key, silently disables read-timeout detection, so a stalled read is reported as a successful but truncated response instead of a timeout — with no error at all for chunked or Connection: close bodies. It must also close the underlying resource when closed, or each request leaks a socket or file descriptor (including HEAD and stream => true responses, which are not drained), eventually exhausting the descriptor limit. The stream must be readable for gzip/deflate decoding, and should stream the resource rather than buffer it entirely into memory. The full contract is described under Creating Streams in the PSR-7 stream documentation. Supply a stream implementation you trust.

uri_factory

Summary PSR-17 URI factory used when Guzzle creates URI objects from strings.

Types Psr\Http\Message\UriFactoryInterface

Default GuzzleHttp\Psr7\HttpFactory

Constant GuzzleHttp\RequestOptions::URI_FACTORY

php
$factory = new \GuzzleHttp\Psr7\HttpFactory();

$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.example.com',
    'uri_factory' => $factory,
]);

This option can be set on a client or per request. It is used for string request URI values, string base_uri values, and redirect Location header parsing when redirects are enabled. URI objects implementing Psr\Http\Message\UriInterface are used as provided.

[!NOTE] This option affects request-side URI creation only. It does not affect response implementations returned by handlers. GuzzleHttp\Client::sendRequest() still returns redirect responses as-is for PSR-18 compliance.

[!WARNING] Guzzle only checks that the value implements Psr\Http\Message\UriFactoryInterface; it does not validate the URIs it returns. When following redirects Guzzle strips credentials by comparing the origin (scheme, host, port) of the current and redirect-target URIs: on a cross-origin redirect it removes the Authorization and Cookie headers and clears HTTP auth, and it drops Referer whenever the scheme changes (including an https to http downgrade) and otherwise sends only the origin on cross-origin redirects — all by reading getScheme(), getHost(), and getPort() from the URI built from the Location header. A custom URI that misreports those, or whose getters disagree with the address actually dialed, can make a cross-origin redirect look same-origin and leak credentials to the target (the class of issue behind CVE-2022-31042, CVE-2022-31043, CVE-2022-31090, and CVE-2022-31091), or enable SSRF and protocol allow-list bypass. The default GuzzleHttp\Psr7\Uri lower-cases and validates the scheme and host and strips default ports; supply a URI implementation you trust.

sink

Summary Specify where the body of a response will be saved.

Types

  • string (path to file on disk)
  • fopen() resource
  • Psr\Http\Message\StreamInterface

Default PHP temp stream

Constant GuzzleHttp\RequestOptions::SINK

Pass a string to specify the path to a file that will store the contents of the response body:

php
$client->request('GET', '/stream/20', ['sink' => '/path/to/file']);

Pass a resource returned from fopen() to write the response to a PHP stream:

php
$resource = \GuzzleHttp\Psr7\Utils::tryFopen('/path/to/file', 'w');
$client->request('GET', '/stream/20', ['sink' => $resource]);

Pass a Psr\Http\Message\StreamInterface object to stream the response body to an open PSR-7 stream.

php
$resource = \GuzzleHttp\Psr7\Utils::tryFopen('/path/to/file', 'w');
$stream = \GuzzleHttp\Psr7\Utils::streamFor($resource);
$client->request('GET', '/stream/20', ['sink' => $stream]);

With Guzzle's built-in cURL and PHP stream handlers, non-streaming responses use the sink stream as the response body. The handlers rewind seekable sink streams before returning the response. If the sink is not seekable, the request still succeeds and the response body is left at the sink's current position, usually EOF.

Digest authentication buffers intermediate challenge bodies away from a configured sink. A terminal 401 response, such as authentication failure or no usable Digest challenge, is the final response and its body is restored into the configured sink. Non-challenge response bodies, including redirect-hop bodies, follow normal sink behavior; with resource or stream sinks, redirect-hop bodies may be written before the final response body for Digest and non-Digest requests alike.

If sink is a string path, Guzzle opens the file and owns that stream.

If sink is a PHP resource, the caller owns the resource and is responsible for closing it. Closing or destroying the response body detaches Guzzle's wrapper without closing the original resource.

If sink is a Psr\Http\Message\StreamInterface, Guzzle uses that stream object as-is and its own close() behavior applies.

ssl_key

Summary Specify the path to a file containing a private SSL key. PEM is the default private key format. If a password is required, then set to an array containing the path to the SSL key in the first array element followed by the password required for the key in the second element. A null password is treated the same as omitting it. Use ssl_key_type to specify another supported key format.

Types

  • string
  • array

Default None

Constant GuzzleHttp\RequestOptions::SSL_KEY

[!NOTE] With the stream handler, cert and ssl_key must use the same passphrase when both options specify one because PHP streams expose only one SSL context passphrase.

[!NOTE] TLS client key options remain active during redirects. See Cross-Origin Redirects for details.

ssl_key_type

Summary Specify the SSL private key file type.

Types

  • string

Default PEM

Constant GuzzleHttp\RequestOptions::SSL_KEY_TYPE

php
$client->request('GET', '/', [
    'ssl_key' => '/path/client.key',
    'ssl_key_type' => 'DER',
]);

The cURL handler passes this value to CURLOPT_SSLKEYTYPE. Supported values depend on libcurl and its TLS backend.

[!NOTE] The stream handler supports only PEM private key files.

stream

Summary Set to true to stream a response rather than download it all up-front.

Types bool

Default false

Constant GuzzleHttp\RequestOptions::STREAM

php
$response = $client->request('GET', '/stream/20', ['stream' => true]);
// Read bytes off of the stream until the end of the stream is reached
$body = $response->getBody();
while (!$body->eof()) {
    echo $body->read(1024);
}

[!NOTE] Streaming response support must be implemented by the HTTP handler used by a client. This option might not be supported by every HTTP handler, but the interface of the response object remains the same regardless of whether or not it is supported by the handler.

With Guzzle's default handler selection, streamed responses use the stream handler when PHP streams are available. The timeout deadline then applies to obtaining the response and does not bound reads on the streamed body: each read is bounded by the read_timeout idle timeout, and a timed-out read throws GuzzleHttp\Psr7\Exception\TimeoutException and can be retried. The built-in cURL handlers download the body up-front even when stream is enabled, so the deadline covers the whole transfer.

Handlers that do not support stream fall back to sink behavior. Digest authentication still protects configured sinks from intermediate challenge bodies. With StreamHandler, stream => true, and a configured sink, a Digest request drains the final body into the sink while a non-Digest request leaves the sink untouched.

The built-in stream handler rejects stream => true when the max_host_connections or max_total_connections constructor options are configured because streamed connections cannot be capped; stream => false and omitting the option remain valid, and the numeric values provide no stream-handler admission control. See How can I limit concurrent connections? for the connection cap semantics.

stream_context

Summary PHP stream context options to merge into the context used by the built-in stream handler.

Types array

Default None

Constant GuzzleHttp\RequestOptions::STREAM_CONTEXT

This option is only supported by the built-in stream handler. Built-in cURL handlers reject this option because cURL does not use PHP stream contexts.

Stream context options outside the built-in stream handler allow-list, or not available in the current PHP runtime, are rejected. Allow-listing means Guzzle passes the option through; PHP or OpenSSL may still reject or ignore an option depending on the runtime. The allow-list is http.request_fulluri, socket.bindto, socket.tcp_nodelay, ssl.SNI_enabled, ssl.capture_peer_cert, ssl.capture_peer_cert_chain, ssl.ciphers, ssl.disable_compression, ssl.no_ticket, ssl.peer_fingerprint, ssl.security_level, and ssl.verify_depth. Use Guzzle request options instead when configuring the request method, URI, body, headers, timeouts, redirects, proxy, TLS certificate files, TLS private keys, protocol versions, verification, progress, debug output, sinks, cookies, and allowed protocols. TLS minimum protocol versions are managed through the crypto_method request option, maximum protocol versions are managed through the crypto_method_max request option, and TLS verification is managed through the verify request option.

synchronous

Summary Set to true to inform HTTP handlers that you intend on waiting on the response. This can be useful for optimizations.

Types bool

Default none

Constant GuzzleHttp\RequestOptions::SYNCHRONOUS

verify

Summary Describes the SSL certificate verification behavior of a request.

  • Set to true to enable SSL certificate verification and use the default CA bundle provided by operating system.
  • Set to false to disable certificate verification (this is insecure!).
  • Set to a string to provide the path to a CA bundle to enable verification using a custom certificate.

Types

  • bool
  • string

Default true

Constant GuzzleHttp\RequestOptions::VERIFY

php
// Use the system's CA bundle (this is the default setting)
$client->request('GET', '/', ['verify' => true]);

// Use a custom SSL certificate on disk.
$client->request('GET', '/', ['verify' => '/path/to/cert.pem']);

// Disable validation entirely (don't do this!).
$client->request('GET', '/', ['verify' => false]);

If you do not need a specific certificate bundle, then Mozilla provides a commonly used CA bundle which can be downloaded here (provided by the maintainer of cURL). Once you have a CA bundle available on disk, you can set the "openssl.cafile" PHP ini setting to point to the path to the file, allowing you to omit the "verify" request option. Much more detail on SSL certificates can be found on the cURL website.

timeout

Summary Number of seconds to use as the total timeout of the request. Use 0 to disable the total timeout (the default behavior). Positive values below 0.001 seconds are rejected by the built-in handlers.

Types

  • int
  • float

Default 0

Constant GuzzleHttp\RequestOptions::TIMEOUT

php
// Timeout if the request does not complete in 3.14 seconds.
$client->request('GET', '/delay/5', ['timeout' => 3.14]);

The built-in cURL handlers enforce the timeout for the whole transfer. The built-in stream handler enforces it while connecting and while buffering the response: the transfer is aborted once the deadline passes. While waiting for response headers the stream handler can only bound the time between packets, so a response whose header block arrives in small pieces past the deadline is rejected as soon as the headers complete. With the stream option enabled, the deadline applies to obtaining the response and does not bound reads on the streamed body.

The deadline never governs how long the connection may sit silent; that is the role of read_timeout, the idle timeout, which applies at every stage of a stream handler request and defaults to 60 seconds. The stream handler does not consult the default_socket_timeout ini setting.

Built-in handlers use the most specific transport timeout exception they can determine. Connect timeouts throw GuzzleHttp\Exception\ConnectTimeoutException, which extends ConnectException. Other transport timeouts before a response is received throw GuzzleHttp\Exception\NetworkTimeoutException. Transport timeouts after a response is received throw GuzzleHttp\Exception\ResponseTimeoutException, which extends GuzzleHttp\Exception\ResponseTransferException and exposes the response.

Timeouts from caller-supplied PSR-7 streams are not transport timeouts. A request body stream timeout while detecting size, buffering, rewinding, or reading upload bytes throws GuzzleHttp\Exception\RequestException before a response and GuzzleHttp\Exception\ResponseException after response headers. A slow response sink write also throws ResponseException.

Timeout detection is best-effort: it relies on the stream exposing PHP's timed_out metadata. The network socket Guzzle opens for the stream handler exposes this metadata, but a caller-supplied request-body or sink stream might not, for example a custom StreamInterface implementation. When the metadata is absent, the failure is not recognized as a timeout and surfaces as an ordinary read/write error instead.

Timeout phases

The following table shows the timeouts that bound each phase of a request sent by the built-in handlers and the exception thrown when one is exceeded. The deadline is this timeout option; the idle timeout is read_timeout.

PhasecURL handlersStream handler
ConnectingBounded by connect_timeout and the deadline; times out with ConnectTimeoutExceptionBounded by the idle timeout and the deadline; times out with ConnectTimeoutException
Receiving headersBounded by the deadline; aborted mid-headers with NetworkTimeoutExceptionPacket gaps are bounded by the idle timeout and the remaining deadline; a header block completing past the deadline is rejected with ResponseTimeoutException
Body, buffered (default)Bounded by the deadline; aborted with ResponseTimeoutExceptionBounded by the deadline and, on each read, the idle timeout; aborted with ResponseTimeoutException
Body, stream enabledThe body is downloaded up-front regardless, so the deadline applies as aboveNot bounded by the deadline; each read is bounded by the idle timeout, and a timed-out read throws a retryable GuzzleHttp\Psr7\Exception\TimeoutException

version

Summary Protocol version to attempt to use with the request.

Types string, int, float

Default 1.1

Constant GuzzleHttp\RequestOptions::VERSION

php
// Force HTTP/1.0
$request = $client->request('GET', '/get', ['version' => 1.0]);

// Attempt HTTP/2 with the cURL handler
$request = $client->request('GET', 'https://example.com', ['version' => 2.0]);

// Attempt HTTP/3 with the cURL handler
$request = $client->request('GET', 'https://example.com', ['version' => 3.0]);

Guzzle defaults to HTTP/1.1. Set version when you want a request to use or attempt a different HTTP protocol version. Guzzle does not opt requests into HTTP/2 or HTTP/3 automatically just because the installed cURL stack supports them.

The built-in stream handler supports only 1.0 and 1.1. The built-in cURL handler supports 1.0, 1.1, 2.0, and 3.0, but HTTP/2 and HTTP/3 depend on the PHP cURL extension and the linked runtime libcurl capabilities.

Empty or malformed version values are rejected before the request is sent. If the value is well-formed but the selected handler cannot use it, the transfer fails with GuzzleHttp\Exception\RequestException. For example, the stream handler rejects HTTP/2 and HTTP/3, the cURL handler rejects HTTP/2 when libcurl does not report HTTP/2 support, and the cURL handler rejects HTTP/3 when the installed cURL stack does not report HTTP/3 support.

For cURL requests, version is converted to Guzzle-managed cURL options. Use this request option instead of passing raw CURLOPT_HTTP_VERSION; built-in cURL handlers reject raw cURL options that conflict with Guzzle-managed protocol handling.

HTTP/2 uses libcurl's CURL_HTTP_VERSION_2_0. HTTP/3 uses libcurl's CURL_HTTP_VERSION_3 unless multiplex is set to Multiplexing::REQUIRE_EAGER or Multiplexing::REQUIRE_WAIT, which use libcurl's strict HTTP/3-only mode. The non-required modes ask libcurl to attempt the requested protocol, but they are not strict modes: libcurl may use a lower HTTP version when negotiation or connection setup falls back. The response protocol version can therefore be lower than the version value you requested.

When multiple HTTP/2- or HTTP/3-capable requests start concurrently against the same origin, the multiplex request option controls whether they wait to share one connection instead of each opening their own.

HTTP/3 support requires PHP to expose cURL's HTTP/3 constants, runtime libcurl 7.88.0 or higher, and runtime libcurl reporting the CURL_VERSION_HTTP3 feature. A libcurl version number is not enough by itself: libcurl must also be built with HTTP/3 and QUIC support, commonly through an HTTP/3 backend such as ngtcp2 with nghttp3 or quiche.

A request configured with version => 3.0 must pass HTTP/3 support checks even if it uses a proxy. If a proxy is actually selected, whether through the proxy option or resolved from the environment, Guzzle does not try HTTP/3 through the proxy in non-required modes; it sends the transfer as HTTP/2 when available, otherwise HTTP/1.1. Required multiplexing rejects proxied HTTP/3 because it cannot guarantee HTTP/3 through the proxy. A matching proxy no rule or environment no_proxy entry makes the request direct, so HTTP/3 can still be attempted.

For HTTPS cURL requests, Guzzle sets a minimum of TLS 1.2 by default. That minimum also applies when HTTP/2 or HTTP/3 is requested, because cURL may fall back to a TLS-based HTTP/1.1 or HTTP/2 connection. If you set crypto_method to TLS 1.3, Guzzle keeps that stricter setting when the cURL stack exposes TLS 1.3 configuration. Raw CURLOPT_SSLVERSION values passed through the curl option are rejected because TLS version handling is managed by Guzzle. The crypto_method_max request option can cap the maximum TLS version, but the cap must not be lower than the effective minimum (TLS 1.2 by default for HTTPS unless crypto_method is set lower).