Installation
Require the package. The service provider and the SafeFetch facade alias are auto-discovered, and nothing else is needed — no database, no migrations:
composer require roundly-consulting/safe-fetch-for-laravelThe defaults are strict: https only, any port, the blocked-network table, the app’s own host names refused and redirects off. Send requests with the curl extension installed — DNS pinning depends on it (see Requirements).
Validate a URL, then fetch it
Validate the URL a user hands in, then fetch it — capped, with errors that say whether a retry can help:
use Illuminate\Support\Facades\Http;
use RoundlyConsulting\SafeFetch\Exceptions\SafeFetchException;
use RoundlyConsulting\SafeFetch\Facades\SafeFetch;
use RoundlyConsulting\SafeFetch\Rules\SafeUrl;
$request->validate(['feed' => ['required', 'string', 'max:2048', new SafeUrl]]);
try {
$feed = SafeFetch::fetch($request->input('feed'), maxBytes: 2_000_000);
// $feed->body, $feed->contentType, $feed->url
} catch (SafeFetchException $e) {
$e->retryable(); // a 5xx or a timeout can heal; a blocked address never does
}Or guard any Laravel HTTP call:
Http::safe()->withToken($token)->post($hookUrl, $payload);Publish the config and translations (optional)
php artisan vendor:publish --tag=safe-fetch-config # config/safe-fetch.php
php artisan vendor:publish --tag=safe-fetch-translations # lang/vendor/safe-fetchLocal development
To reach services on your own machine while developing, allow http and the loopback address you need — never in production:
// config/safe-fetch.php
'allow_http' => env('SAFE_FETCH_ALLOW_HTTP', false), // SAFE_FETCH_ALLOW_HTTP=on in .env
'ranges' => ['allowed' => ['127.0.0.1/32'], 'blocked' => []],Check the environment
php artisan safe-fetch:checkIt prints whether DNS pinning is available (and the libcurl version), whether the pre-send address check is on, whether international host names work, the Guzzle version and whether the configuration is valid, and it warns about settings that weaken the guard. It exits 1 on an error-level finding — no curl, an invalid configuration. See Artisan commands.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.