caseyamcl/guzzle_retry_middleware
Guzzle Retry Middleware automatically retries Guzzle HTTP requests when servers return 429 or 503, or when requests time out. It honors Retry-After headers, backs off exponentially, supports configurable retry limits and callbacks, and drops into your handler stack with no setup.
Retry-After, exponential back-off when the header is absent, and a cap on attempts.Retry-After, honoring it is a contractual and reputational requirement. This middleware makes compliance the default rather than a per-integration negotiation.on_retry_callback is the hook for metrics (retry count by host/status), which supports decisions like "should we cache this vendor response?" or "is this integration degrading?"retry_enabled => false for non-idempotent calls).Adopt when:
429/503 defaults, Retry-After parsing (including date format), exponential fallback, max attempts.retry_on_timeout, Curl error 104) — the built-in Guzzle middleware does not cover this breadth.retry_enabled, max_retry_attempts => 0, retry_on_methods, should_retry_callback) so that safe methods retry and non-idempotent ones do not.on_retry_callback and expose_retry_header.Look elsewhere or pause when:
^2. Confirm the matrix before committing — this is the single biggest adoption risk.Http::retry() already covers the need. If retry semantics are simple and confined to Laravel's HTTP client, the framework feature may be sufficient; this package earns its place when you need Retry-After parsing, timeout retries, callbacks, or a raw Guzzle stack.give_up_after_secs/max_allowable_timeout_secs and pair with a breaker if that's the actual need.retry_on_methods or disable per request.For executives: This is a small, free, MIT-licensed component that makes our outbound API calls retry automatically when a partner is briefly unavailable or rate-limiting us. It converts a class of user-visible failures and on-call pages into silent, bounded recoveries — measured in minutes of engineering time rather than a project. Cost is one Composer dependency; risk is low (active releases, high test coverage, permissive license) with one caveat: we must verify Guzzle version compatibility before rollout, and it does not replace monitoring or circuit breaking.
For engineering: Drop GuzzleRetryMiddleware::factory() onto the handler stack and you get spec-aligned defaults: retry on 429/503, honor Retry-After when present, exponential back-off (1.5 × attempt) when it isn't, 10-attempt ceiling, optional timeout/connection-reset retries, and an on_retry_callback for structured logging and metrics. Configuration resolves predictably — per-request beats client constructor beats middleware factory — so we can set a platform-wide default and still let individual features opt out (retry_enabled => false) or narrow behavior (retry_on_methods, retry_on_status, retry_only_if_retry_after_header). Two action items before we ship it: pin the correct major (^2 for Guzzle 6/7, current release for Guzzle 8) and set max_allowable_timeout_secs / give_up_after_secs so retries can't stack into long-held workers. Add an on_retry_callback that emits a counter tagged by host and status — that single hook gives us the data to decide later whether a partner needs caching, a breaker, or a renegotiated rate limit.
How can I help you explore Laravel packages today?