Table of Contents
📖 2 minutes read
Need to know the final URL after following redirects? Don’t parse the response—use Guzzle’s TransferStats callback to capture the effective URI automatically.
The Use Case
You’re processing shortened URLs (like bit.ly links) and need to extract the final slug or canonical URL after all redirects resolve.
The Solution
use GuzzleHttp\TransferStats;
use Illuminate\Support\Facades\Http;
$effectiveUrl = null;
Http::withOptions([
'on_stats' => function (TransferStats $stats) use (&$effectiveUrl) {
$effectiveUrl = (string) $stats->getEffectiveUri();
},
])->head($shortenedUrl);
// $effectiveUrl is now 'https://example.com/articles/full-guide'
$slug = basename(parse_url($effectiveUrl, PHP_URL_PATH));
// $slug = 'full-guide'
How It Works
on_statsis a Guzzle option that registers a callback- The callback runs after the request completes
TransferStatscontains metadata about the request, including the final URIgetEffectiveUri()returns the URL after all redirects
Why HEAD Instead of GET?
Using head() instead of get() makes this efficient—you only need the final destination, not the response body. The server follows redirects and returns headers, but skips sending the full content.
Result: Fast, bandwidth-efficient redirect resolution.
What Else Is in TransferStats?
Http::withOptions([
'on_stats' => function (TransferStats $stats) {
dump([
'effective_uri' => $stats->getEffectiveUri(),
'transfer_time' => $stats->getTransferTime(), // seconds
'redirect_count' => $stats->getRedirectCount(),
]);
},
])->head($url);
You can also access:
getHandlerStats()— low-level curl stats (DNS lookup time, connect time, etc.)getRequest()— the original request objectgetResponse()— the final response object (if available)
Real-World Applications
- Canonical URL discovery: Resolve short links to full URLs for storage
- Slug extraction: Extract clean slugs from user-submitted URLs
- Redirect chain analysis: Track how many hops it took to reach the final destination
- Performance monitoring: Log transfer times and redirect counts for external APIs
Bonus: Caching Redirects
Since redirect resolution is deterministic, you can cache the effective URL:
$cacheKey = 'redirect_'.md5($shortenedUrl);
return Cache::remember($cacheKey, now()->addDay(), function () use ($shortenedUrl) {
$effectiveUrl = null;
Http::withOptions([
'on_stats' => fn(TransferStats $stats) =>
$effectiveUrl = (string) $stats->getEffectiveUri()
])->head($shortenedUrl);
return $effectiveUrl;
});
Now you only resolve each shortened URL once, even across multiple requests.
Leave a Reply