Weave 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

Laravel Safe Browsing Laravel Package

ariaieboy/laravel-safe-browsing

A Laravel package that integrates Google Safe Browsing to check URLs for malware, phishing, and unwanted software. It offers simple configuration and a clean Laravel-native API for validating links before redirects or accepting user-submitted content.

View on GitHub
Deep Wiki
Context7

Getting Started

ariaieboy/laravel-safe-browsing is a thin Laravel wrapper around Google's Safe Browsing Lookup API (threatMatches:find). You give it a URL (or several), it tells you whether Google currently flags it as malware, social engineering (phishing), unwanted software, or a potentially harmful application.

Minimum steps:

composer require ariaieboy/laravel-safe-browsing
php artisan vendor:publish --tag="laravel-safe-browsing-config"

Then add your key to .env:

SAFE_BROWSING_API_KEY=your-google-api-key

The key comes from Google Cloud Console — you must enable the Safe Browsing API on the project, and the key is restricted by API, not by referrer (server-side calls).

Where to look first: the published config/safe-browsing.php (key name, default threat types, timeout/endpoint settings if exposed) and the package facade. The facade is the whole public surface — read its method signatures before designing around it; the package is small enough that the facade plus config is the entire API.

First real use case: validating user-submitted links. A "post a link" form, a profile website field, a webhook URL — anywhere an attacker can get your domain to host or redirect to a phishing page. Start with a single synchronous check in a Form Request and expand from there.

Implementation Patterns

Custom validation rule (the 80% case). Wrap the check in a rule so it composes with the rest of your validation and is trivially testable:

class SafeUrl implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (! LaravelSafeBrowsing::isSafe($value)) {
            $fail('This URL has been flagged as unsafe.');
        }
    }
}

Confirm the exact method name against the README — the package exposes a boolean-style check, and you want the boolean form rather than the raw match array for validation.

Wrap it, don't sprinkle it. Put a single SafeBrowsingService (or a small UrlSafety class) between your app and the facade. That gives you one place to add caching, logging, retry, and a try/catch for transport failures, and it means a future package swap or a move to the v5 API is a one-file change. Inject that class, not the facade, into controllers and jobs.

Never call it inline on a hot path. The lookup is an outbound HTTP request. On a form POST it's acceptable (the user is waiting anyway and you get a hard block). On page render, a redirect middleware, or anything in a loop, push it to a queued job and act on the result asynchronously — e.g. store the URL with status = pending, scan in a job, flip to blocked and notify.

Cache both directions, asymmetrically. Cache safe results for a meaningful window (hours to a day — reputation doesn't change second to second) and unsafe results for longer, or not at all if you want to re-check on appeal. Key the cache on the normalized URL, not the raw input.

Batch where the API allows it. The underlying endpoint accepts multiple URLs per request. If you're sweeping a table of stored links (a nightly php artisan urls:scan command), chunk the IDs and send batches rather than one HTTP call per row — this is the difference between a 30-second command and a rate-limit ban.

Middleware for outbound redirects. If you run a link shortener or an outbound redirector (/go/{link}), do the check at creation time and keep an is_flagged column you can flip later; you do not want a synchronous Google call in the redirect path.

Testing. Use Http::fake() against the Safe Browsing endpoint and assert both the "match returned" and "empty matches" shapes — plus a faked 500. Your service should be tested for all three, because the failure branch is where the real design decision lives.

Gotchas and Tips

Laravel 12 support (1.5.0) is the headline of this release, and it's a non-event technically. The change widens the illuminate/* constraint to include ^12.0. There are no API changes, no deprecations, no config changes. Upgrading from 1.4.0 on an existing Laravel 10/11 app is a no-op; the only requirement is that your app meets Laravel 12's PHP 8.2+ floor. If you pinned 1.4.* in composer.json, loosen to ^1.5 — otherwise a future Laravel 12 upgrade will fight you on dependency resolution.

An empty response is not the same as a successful response. The API returns matches only; no matches means safe. That makes it dangerously easy to write if (empty($matches)) return true; and accidentally treat a 429, a timeout, or an invalid-key 400 as "safe". Decide explicitly whether you fail open (let the URL through, log it) or fail closed (reject, risk blocking legitimate users during a Google outage). Most apps should fail open on transport errors but log loudly, and never fail open on a malformed-key error — that's a misconfiguration, not an outage, and it will silently never protect you.

The API key is a billing/quota surface. The Safe Browsing lookup has a free tier but is rate-limited; an unguarded loop over 50k rows will get you throttled and then your fail-open path silently approves everything. Combine batching, caching, and a queued/scheduled sweep with backoff.

Normalize URLs before checking. Google evaluates the canonical form. HTTP://Example.com/../a and http://example.com/a should resolve to the same cache key and the same lookup; run input through a URL parser and rebuild it, and reject non-http(s) schemes before you ever spend a request.

Threat types are configurable and the defaults may be broader or narrower than you want. POTENTIALLY_HARMFUL_APPLICATION is about downloaded executables and is mostly noise for a web-link field; SOCIAL_ENGINEERING is the one that matters for phishing. Trim the list in config rather than post-filtering the response.

Debugging: wrap the call and log the request URL (redacting the key), the HTTP status, and the raw match array on failure. If checks always return safe in production but work locally, the usual culprits are, in order: key not enabled for the Safe Browsing API in that GCP project, key restricted to the wrong API, or .env cached (php artisan config:clear).

Extension point: the package is small — if you need retry/backoff, a different HTTP client, or the newer v5 urls:search semantics, fork the service class behind your own wrapper rather than fighting the facade. Because you isolated it behind SafeBrowsingService, that's a contained change.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource