Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Guzzle Redirect History Middleware Laravel Package

spatie/guzzle-redirect-history-middleware

Guzzle middleware that records redirect chains for each request. Attach RedirectHistoryMiddleware to your handler stack to capture every Location hop and inspect the final URL and intermediate redirects via a RedirectHistory instance.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/guzzle-redirect-history-middleware
    

    Add the middleware to your Guzzle client stack:

    use Spatie\GuzzleRedirectHistoryMiddleware\RedirectHistoryMiddleware;
    
    $client = new Client([
        'middleware' => [
            RedirectHistoryMiddleware::class,
        ],
    ]);
    
  2. First Use Case: Track redirects in a simple request:

    $response = $client->get('https://example.com/redirect-chain');
    $redirects = $client->getConfig('redirect_history'); // Access via config
    // or via middleware instance (if stored)
    

Implementation Patterns

Core Workflow

  1. Middleware Integration:

    • Attach to Guzzle client via middleware config array.
    • Works seamlessly with existing middleware stacks (e.g., RetryMiddleware, JsonMiddleware).
  2. Accessing Redirect History:

    • Via Client Config:
      $history = $client->getConfig('redirect_history');
      
    • Via Middleware Instance (if stored):
      $middleware = $client->getConfig('middleware')[0];
      $history = $middleware->getRedirectHistory();
      
  3. Custom Storage:

    • Override storage behavior by extending RedirectHistoryMiddleware:
      class CustomMiddleware extends RedirectHistoryMiddleware {
          public function __construct() {
              $this->history = collect(); // Use Laravel Collections
          }
      }
      

Common Use Cases

  • Debugging Redirects: Log redirects in tests or production:

    $client->get('https://api.example.com')->then(function ($response) {
        $redirects = $response->getClient()->getConfig('redirect_history');
        Log::debug('Redirect chain:', $redirects);
    });
    
  • Conditional Logic: Use redirect history to enforce policies (e.g., block external redirects):

    $response = $client->get('https://example.com');
    $redirects = $response->getClient()->getConfig('redirect_history');
    
    if ($redirects->contains(function ($redirect) {
        return str_starts_with($redirect['uri'], 'https://external.com');
    })) {
        throw new \RuntimeException('External redirect detected!');
    }
    
  • Testing: Assert redirect behavior in PHPUnit:

    $response = $client->get('https://example.com/redirect');
    $this->assertCount(2, $response->getClient()->getConfig('redirect_history'));
    

Gotchas and Tips

Pitfalls

  1. Middleware Order:

    • Place RedirectHistoryMiddleware before other middleware that might modify responses (e.g., HistoryMiddleware).
    • Example:
      'middleware' => [
          RedirectHistoryMiddleware::class,
          HistoryMiddleware::class, // Will capture final response
      ],
      
  2. History Persistence:

    • Redirect history is not automatically stored in the response. Always fetch it from the client config or middleware instance.
    • Fix: Store the middleware instance in a container if needed:
      $client->getConfig('redirect_middleware') = $middleware;
      
  3. Loop Detection:

    • The middleware does not detect infinite redirect loops. Use Guzzle’s built-in allow_redirects config to limit loops:
      $client = new Client([
          'allow_redirects' => [
              'max' => 5,
          ],
      ]);
      

Debugging Tips

  • Inspect Raw History: The history array contains:

    [
        [
            'uri' => 'https://example.com/old',
            'status' => 301,
            'headers' => ['Location' => 'https://example.com/new'],
        ],
        // ...
    ]
    

    Use dd() or Log::debug() to inspect.

  • Disable Middleware Temporarily: Remove it from the stack to isolate issues:

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

Extension Points

  1. Custom History Format: Override getRedirectHistory() to transform data:

    class CustomMiddleware extends RedirectHistoryMiddleware {
        public function getRedirectHistory() {
            return $this->history->map(function ($redirect) {
                return [
                    'from' => $redirect['uri'],
                    'to' => $redirect['headers']['Location'] ?? null,
                ];
            });
        }
    }
    
  2. Event-Based Hooks: Extend to trigger events on redirects:

    class EventMiddleware extends RedirectHistoryMiddleware {
        public function __construct() {
            $this->history = [];
            event(new RedirectStarted('https://example.com'));
        }
    
        protected function recordRedirect($request, $response) {
            parent::recordRedirect($request, $response);
            event(new RedirectCompleted($response->getEffectiveUri()));
        }
    }
    
  3. Laravel Integration: Bind the middleware to the container for easy access:

    $app->singleton('redirectHistoryMiddleware', function ($app) {
        return new RedirectHistoryMiddleware();
    });
    

    Then inject it into services:

    public function __construct(private RedirectHistoryMiddleware $middleware) {}
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport