PHPackages                             zengapay/guzzle\_retry\_middleware - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. zengapay/guzzle\_retry\_middleware

ActiveLibrary[HTTP &amp; Networking](/categories/http)

zengapay/guzzle\_retry\_middleware
==================================

Guzzle v6+ retry middleware that handles status codes other than 200, 201, 202 and connection timeouts

0842PHP

Since Oct 26Pushed 4y ago1 watchersCompare

[ Source](https://github.com/zengapay/guzzle-retry-middleware)[ Packagist](https://packagist.org/packages/zengapay/guzzle_retry_middleware)[ RSS](/packages/zengapay-guzzle-retry-middleware/feed)WikiDiscussions main Synced 2d ago

READMEChangelogDependenciesVersions (1)Used By (0)

Guzzle Retry Middleware
=======================

[](#guzzle-retry-middleware)

[![Latest Version on Packagist](https://camo.githubusercontent.com/137d68fead90bc12a274ceb9cfbc429a581368b8cbf4efd48d74a681378d3c13/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6361736579616d636c2f67757a7a6c655f72657472795f6d6964646c65776172652e7376673f7374796c653d666c6174)](https://packagist.org/packages/caseyamcl/guzzle_retry_middleware)[![Software License](https://camo.githubusercontent.com/f251623e510f5909f16ae3f4e6e548dac11340b9fde1a99be26b015b39272c00/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c6174)](LICENSE.md)[![Github Build](https://github.com/caseyamcl/guzzle_retry_middleware/workflows/Github%20Build/badge.svg)](https://github.com/caseyamcl/guzzle_retry_middleware/actions?query=workflow%3A%22Github+Build%22)[![Code coverage](https://github.com/caseyamcl/guzzle_retry_middleware/raw/master/coverage.svg)](coverage.svg)[![PHPStan Level 8](https://camo.githubusercontent.com/f60d96f7c2579690ab6dfa8918f777fe93a02a92301c661eb38a85861a92b780/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e2e7376673f7374796c653d666c6174)](https://phpstan.org/)[![Total Downloads](https://camo.githubusercontent.com/99b4e3d6ef5f482ad40a575f13ad3cc7c3ec9c97f268be168d02d055920301ea/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6361736579616d636c2f67757a7a6c655f72657472795f6d6964646c65776172652e7376673f7374796c653d666c6174)](https://packagist.org/packages/caseyamcl/guzzle_retry_middleware)

This is a [Guzzle v6+](http://guzzlephp.org) middleware library that implements automatic retry of requests when HTTP servers respond with status codes other than `200`, `201`, `202`. It can also be configured to retry requests that timeout.

If a server supplies a [Retry-After header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After), this middleware will delay subsequent requests per the server's instructed wait period.

Unlike the built-in `RetryAfter` middleware, this middleware provides some default behavior for negotiating retries based on rules in the HTTP Spec. You can drop it right into your request stack without any additional configuration.

Features, at-a-glance:

- Automatically retries HTTP requests when a server responds with a status other than `200`, `201`, `202` (or any HTTP status code; this is configurable)
- Sets a retry delay based on the `Retry-After` HTTP header, if it is sent, or automatically backs off exponentially if no `Retry-After` header is sent (also configurable)
- Optionally retries requests that time out (via the `connect_timeout` or `timeout` options)
- Set an optional callback when a retry occurs (useful for logging/reporting)
- Specify a maximum number of retry attempts before giving up (default: 10)
- Near-100% test coverage, good inline documentation, and PSR-12 compliant

Install
-------

[](#install)

Via Composer

```
$ composer require zengapay/guzzle_retry_middleware
```

Usage
-----

[](#usage)

Basically:

```
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleRetry\GuzzleRetryMiddleware;

$stack = HandlerStack::create();
$stack->push(GuzzleRetryMiddleware::factory());

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

// Make requests galore...
```

This is the default configuration. If a HTTP server responds with a status other than `200`, `201`, `202`, this middleware with intercept the response and retry it up to 10 times before giving up and doing whatever behavior Guzzle would do by default (by default, throwing a `BadResponseException`).

If the server provides a `RetryAfter` header, this middleware will wait the specified time before attempting a request again. If not, then it will back off, waiting longer each time between requests until giving up after 10 attempts.

### Options

[](#options)

The following options are available:

OptionTypeDefaultSummary`retry_enabled`booleantrueIs retry enabled (useful for disabling for individual requests)`max_retry_attempts`integer10Maximum number of retries per request`max_allowable_timeout_secs`integernullIf set, specifies a hard ceiling in seconds that the client can wait between requests`retry_only_if_retry_after_header`booleanfalseRetry only if `RetryAfter` header sent`retry_on_status_other_than`array200, 201, 202Retry if server responds with a status other than these`default_retry_multiplier`float or callable1.5Value to multiply the number of requests by if `RetryAfter` not supplied (see [below](#setting-default-retry-delay) for details)`on_retry_callback`callablenullOptional callback to call when a retry occurs`retry_on_timeout`booleanfalseSet to TRUE if you wish to retry requests that throw a ConnectException such as a timeout or 'connection refused'`expose_retry_header`booleanfalseSet to TRUE if you wish to expose the number of retries as a header on the response object`retry_header`stringX-Retry-CounterThe header key to use for the retry counter (if you need it)`retry_after_header`stringRetry-AfterThe header key to use for the retry after header.`retry_after_date_format`string`D, d M Y H:i:s T`Optional customization for servers that return date/times that violate the HTTP specEach option is discussed in detail below.

### Configuring Options

[](#configuring-options)

Options can be set in one of three places:

```
// Per request, in the same array as other Guzzle options
$response = $client->get('/some-url', [
   'max_retry_attempts' => 5,
   'on_retry_callback'  => $notifier
]);

// When you instantiate Guzzle, in the same array as other Guzzle options
$client = new \GuzzleHttp\Client([

    // Standard Guzzle options
    'base_url'        => 'http://example.org',
    'connect_timeout' => 10.0,

    // Retry options
    'max_retry_attempts' => 5,
    'on_retry_callback'  => $notifier
]);

// When you instantiate the Retry middleware
$stack = \GuzzleHttp\Stack::create();
$stack->push(GuzzleRetryMiddleware::factory([
    'max_retry_attempts' => 5,
    'on_retry_callback'  => $notifier
]));
```

If you specify options in two or more places, the configuration is merged as follows:

1. Request options take precedence over Guzzle constructor options
2. Guzzle constructor options take precedence over middleware constructor options.

### Setting maximum retry attempts

[](#setting-maximum-retry-attempts)

This value should be an integer equal to or greater than 0. Setting 0 or a negative effectively disables this middleware.

Setting this value to 0 is useful when you want to retry attempts by default, but disable retries for a particular request:

```
// Set the default retry attempts to 5
$client = new \GuzzleHttp\Client(['max_retry_attempts' => 5]);

// Do not retry this request
$client->get('/some/url', ['max_retry_attempts' => 0]);
```

### Setting status codes NOT to retry

[](#setting-status-codes-not-to-retry)

By default, this middleware will retry requests when the server does not respond with a `200` , `201` or `202` HTTP status code. But, you can configure this:

```
$response = $client->get('/some-path', [
    'retry_on_status_other_than' => [200, 201, 202]
]);
```

If the response includes a `RetryAfter` header, but its status code is not in the list, it will not be processed.

### Setting default retry delay

[](#setting-default-retry-delay)

If the response includes a valid `RetryAfter` header, this middleware will delay the next retry attempt the amount of time that the server specifies in that header.

If the response includes a *non-valid* `RetryAfter` or does not provide a `RetryAfter` header, then this middleware will use a default back-off algorithm: `multipler * number-of-attempts`:

Response with `RetryAfter` header:

```
      Client                 Server
      ------                 ------
      GET /resource    ->

                       get('/some-path', [
    'default_retry_multiplier' => 2.5
]);
```

You can also pass in a custom algorithm for setting the default delay timeout if you specify a callable for `default_retry_multiplier`:

```
// Custom callback to determine default timeout.  Note: $response may be NULL if a connect timeout occurred.
$response = $client->get('/some-path', [
    'default_retry_multiplier' => function($numRequests, ?ResponseInterface $response): float {
        return (float) rand(1, 5);
    }
]);
```

### Retrying requests that timeout

[](#retrying-requests-that-timeout)

You can configure this middleware to retry requests that timeout. Simply set the `retry_on_timeout` option to `true`:

```
# Retry this request if it times out:
$response = $client->get('/some-path', [
    'retry_on_timeout' => true,    // Set the retry middleware to retry when the connection or response times out
    'connect_timeout'  => 20,     // This is a built-in Guzzle option
    'timeout'          => 50      // This is also a built-in Guzzle option
]);

# You can also set these as defaults for every request:
$guzzle = new \GuzzleHttp\Client(['retry_on_timeout' => true, 'connect_timeout' => 20]);
$response = $guzzle->get('https://example.org');
```

### On-Retry callback

[](#on-retry-callback)

You can supply a callback method that will be called each time a request is retried. This is useful for logging, reporting, or anything else you can think of.

If you specify a callback, it will be called before the middleware calls the `usleep()` delay function.

The `request` and `options` arguments are sent by reference in case you want to modify them in the callback.

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

/**
 * Listen for retry events
 *
 * @param int                    $attemptNumber  How many attempts have been tried for this particular request
 * @param float                  $delay          How long the client will wait before retrying the request
 * @param RequestInterface       $request        Request
 * @param array                  $options        Guzzle request options
 * @param ResponseInterface|null $response       Response (or NULL if response not sent; e.g. connect timeout)
 */
$listener = function($attemptNumber, $delay, &$request, &$options, $response) {

    echo sprintf(
        "Retrying request to %s.  Server responded with %s.  Will wait %s seconds.  This is attempt #%s",
        $request->getUri()->getPath(),
        $response->getStatusCode(),
        number_format($delay, 2),
        $attemptNumber
    );
}

$client = new \GuzzleHttp\Client([
    'on_retry_callback' => $listener
]);

$response = $client->get('/some/path');
```

### Enabling or disabling per-request

[](#enabling-or-disabling-per-request)

Suppose that you have setup default retry options as follows:

```
$stack = \GuzzleHttp\Stack::create();
$stack->push(GuzzleRetryMiddleware::factory(['max_retry_attempts' => 5]));
$client = new \GuzzleHttp\Client(['handler' => $stack]);
```

You can disable retry for individual requests as by setting the `retry_enabled` parameter in the request options:

```
// Retry will NOT be attempted for this request..
$client->get('http://example.org', ['retry_enabled' => false]);

// Retry WILL be attempted for this request...
$client->get('http://example.org');
```

### Adding a custom retry header to HTTP responses

[](#adding-a-custom-retry-header-to-http-responses)

Sometimes for debugging purposes, it is useful to know how many times a request was retried when getting a response. For this purpose, this library can add a custom header to responses; simply set the `expose_retry_header` option to `TRUE`.

*Note*: This modifies the HTTP response on the client. If you don't want to alter the response retrieved from the server, you can also use [callbacks](#on-retry-callback) to get the request count.

Example:

```
# Retry this request if it times out:
$response = $client->get('/some-path', [
    'expose_retry_header' => true  // This adds the 'X-Retry-Counter' if a request was retried
]);

# If a request was retried, the response will include the 'X-Retry-Counter'
$numRetries = (int) $response->getHeaderLine('X-Retry-Counter');
```

You can also specify a custom header key:

```
# Retry this request if it times out:
$response = $client->get('/some-path', [
    'expose_retry_header' => true,
    'retry_header'        => 'X-Retry-Count'
]);

# If a request was retried, the response will include the 'X-Retry-Counter'
$numRetries = (int) $response->getHeaderLine('X-Retry-Count');
```

### Modifying the expected header name from `Retry-After`

[](#modifying-the-expected-header-name-from-retry-after)

You can change the header that the client expects the server to respond with. By default, the client looks for the `Retry-After` header, but in some edge-cases, servers may choose to respond with a different header.

```
# Change the name of the expected retry after header to something else:
$response = $client->get('/some-path', [
    'retry_after_header' => 'X-Custom-Retry-After-Seconds'
]);

# Otherwise, the default `Retry-After` header will be used.
$response = $client->get('/some-path');
```

### Setting a custom date format for the `Retry-After` header

[](#setting-a-custom-date-format-for-the-retry-after-header)

You can change the expected date format expected from the server that the client library expects. By default, this library expects an RFC 2822 header as defined in the [HTTP spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date). In certain edge-cases, the server may implement some other date format. This library allows for the possibility of that.

```
# Change the expected date format of the `Retry-After` header
$response = $client->get('/some-path', [
    'retry_after_date_format' => 'Y-m-d H:i:s'
]);

# Otherwise, the default date format for the `Retry-After` header will be used.
# (ex. 'Wed, 24 Nov 2020 07:28:00 GMT')
$response = $client->get('/some-path');
```

*Note*: Be careful not to use this option with the Unix epoch (`u`) format. The client will interpret this value as an integer and subsequently timeout
for a very, very long time.

### Setting a maximum allowable timeout value

[](#setting-a-maximum-allowable-timeout-value)

If you want the client to not accept timeout values greater than a certain value, set the `max_allowable_timeout_secs` option. This will return a static number once the timeout reaches a specified length regardless if it is calculated using the default backoff algorithm or returned from the server via the `Retry-After` header.

By default, this value is `null`, which means there is no limit.

```
# Set the maximum allowable timeout
# If the calculated value exceeds 120 seconds, then just return 120 seconds
$response = $client->get('/some-path', [
    'max_allowable_timeout_secs' => 120
]);
```

Change log
----------

[](#change-log)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Testing
-------

[](#testing)

```
$ composer test
```

*Note:* Since this library tests timeouts, a few of the tests take a 2-3 seconds to run.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Credits
-------

[](#credits)

- [Casey McLaughlin](https://github.com/caseyamcl)
- [Jachim Coudenys](https://github.com/coudenysj)
- [Markus Podar](https://github.com/mfn)
- [All contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity18

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity28

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/67060715?v=4)[ZENGAPAY](/maintainers/zengapay)[@zengapay](https://github.com/zengapay)

---

Top Contributors

[![denismariano](https://avatars.githubusercontent.com/u/27216999?v=4)](https://github.com/denismariano "denismariano (8 commits)")

### Embed Badge

![Health badge](/badges/zengapay-guzzle-retry-middleware/health.svg)

```
[![Health](https://phpackages.com/badges/zengapay-guzzle-retry-middleware/health.svg)](https://phpackages.com/packages/zengapay-guzzle-retry-middleware)
```

###  Alternatives

[php-http/cache-plugin

PSR-6 Cache plugin for HTTPlug

25126.1M82](/packages/php-http-cache-plugin)[illuminate/http

The Illuminate Http package.

11937.9M6.9k](/packages/illuminate-http)[rdkafka/rdkafka

A PHP extension for Kafka

2.2k24.3k1](/packages/rdkafka-rdkafka)[httpsoft/http-message

Strict and fast implementation of PSR-7 and PSR-17

87965.9k114](/packages/httpsoft-http-message)[mezzio/mezzio-router

Router subcomponent for Mezzio

265.4M92](/packages/mezzio-mezzio-router)[serpapi/google-search-results-php

Get Google, Bing, Baidu, Ebay, Yahoo, Yandex, Home depot, Naver, Apple, Duckduckgo, Youtube search results via SerpApi.com

69127.2k](/packages/serpapi-google-search-results-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
