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 Browser Filter Laravel Package

spinen/laravel-browser-filter

Laravel middleware that blocks requests from specified browsers and devices using browser-filter rules. Define allowed or denied user agents in config and redirect or reject unsupported visitors before they reach your routes.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and confirm which Laravel version it supports before wiring anything into production:

composer require spinen/laravel-browser-filter

This is a Laravel 5.x-era package (last release April 2018), so on modern Laravel you may need to register the service provider manually in config/app.php rather than relying on package discovery:

'providers' => [
    // ...
    Spinen\BrowserFilter\BrowserFilterServiceProvider::class,
],

Publish the config and views so you can see the actual rule keys the package expects:

php artisan vendor:publish --provider="Spinen\BrowserFilter\BrowserFilterServiceProvider"

Register the middleware alias in app/Http/Kernel.php:

protected $routeMiddleware = [
    // ...
    'browser.filter' => \Spinen\BrowserFilter\Middleware\BrowserFilter::class,
];

Then apply it to the smallest surface that needs it. The first practical use case is gating a flow that genuinely breaks on old browsers — a checkout, an uploader, a dashboard using modern JS:

// routes/web.php
Route::middleware('browser.filter')->group(function () {
    Route::get('/checkout', 'CheckoutController@show');
});

Before going further, read the published config file and the middleware class itself (vendor/spinen/laravel-browser-filter/src/). The important things to verify are: the browser-name/version rule format, what happens when the user agent can't be identified (fail open or fail closed), and whether the rejection is a redirect or a view.

A minimal config will look roughly like a map of browser names to minimum versions, plus a rejection target:

// config/browser-filter.php (verify exact keys against the package)
return [
    'browsers' => [
        'chrome'  => 50,
        'firefox' => 45,
        'edge'    => 12,
        'ie'      => 11,
        'safari'  => 9,
    ],
    'redirect' => '/browser-unsupported',
];

Implementation Patterns

Tier your enforcement. Don't apply the middleware globally. Use a hard block for critical flows and a soft banner for marketing pages, because a hard redirect on the homepage will hurt conversion and SEO.

// Hard block: only where the app actually breaks
Route::middleware('browser.filter')->group([
    '/checkout',
    '/account/security',
    '/admin',
], function () { /* ... */ });

Exclude the rejection route from the filter. If your unsupported page lives behind the same middleware group, you get a redirect loop. Keep it in the web group only.

Handle JSON/XHR separately. A redirect response to an AJAX call is confusing. Either create a second middleware alias that returns 426 Upgrade Required with a JSON payload, or subclass the middleware and branch on $request->expectsJson():

if ($request->expectsJson()) {
    return response()->json([
        'error' => 'unsupported_browser',
        'message' => 'Please update your browser to continue.',
    ], 426);
}

Use per-route parameters for narrower rules. If the middleware accepts parameters, prefer browser.filter:chrome,firefox,edge on routes where only a subset is supported, rather than editing global config for one page.

Give users an escape hatch. Store an "I understand" cookie and have a thin wrapper middleware skip the check when it's present. This avoids trapping users whose browser is misdetected.

if ($request->cookie('browser_ack')) {
    return $next($request);
}

Build a test around the user agent. This is the cheapest way to catch regressions when you upgrade the package or the config:

public function test_old_ie_cannot_reach_checkout()
{
    $this->withHeader('User-Agent', 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)')
         ->get('/checkout')
         ->assertRedirect('/browser-unsupported');
}

public function test_modern_chrome_can_reach_checkout()
{
    $this->withHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36')
         ->get('/checkout')
         ->assertOk();
}

Log rejections. Add a small override or a listener that logs the blocked user agent. You'll want this data to justify the blocklist and to spot false positives from mobile WebViews.

Keep feature detection alongside it. The middleware is a coarse gate; use @supports in CSS and runtime checks in JS for capabilities. Don't let the PHP middleware become the only thing protecting a JS-only feature.

Gotchas and Tips

  • The detection data is from 2018. Edge Chromium, Brave, Opera GX, Samsung Internet, and many in-app WebViews will be misidentified or unrecognized. Test the "unknown browser" path explicitly — decide whether unknown means allowed or blocked, and make that intentional.
  • User agent strings are trivially spoofed. This is a UX gate, not a security control. Never use it to protect sensitive endpoints; put auth and authorization in front of it.
  • Crawlers get caught in the net. Googlebot and friends often present odd UAs. If you apply the middleware to public pages, whitelist known bots or scope the filter to authenticated areas only. Check Search Console after deploy.
  • Middleware ordering matters. The filter should run after session/auth middleware if it reads cookies, and before controllers that are expensive. Verify with php artisan route:list -v that the alias is actually attached where you think it is.
  • Config caching. After changing rules, run php artisan config:clear (and config:cache in deploy). Rules that work in local often silently don't apply in production because cached config was built before you published the file.
  • Don't cache HTML by UA. If you use a full-page response cache, the "unsupported" page can be served to supported users (or vice versa). Vary the cache key by user agent or exclude these routes.
  • Redirects break API clients. As above, branch on expectsJson() or use a separate alias returning 426.
  • Package age and dependency conflicts. The 2018 release is pinned to old Illuminate versions. On Laravel 9+ you may need to fork it, or skip the wrapper and use hisorange/browser-detect or jaybizzle/crawler-detect directly with a 20-line custom middleware. The wrapper's value is the config-driven rule set, not the detection itself.
  • View namespace. If the package ships a view, it's likely referenced as browser-filter::.... Publish views (vendor:publish --tag=views or the provider tag) before customizing, or you'll be editing inside vendor/.
  • Extension points. Prefer subclassing the middleware and binding your class to the alias over editing vendor code. Override the rejection response in one place so a future package swap only touches that method.
  • Debugging a rule that "doesn't fire." Drop a temporary Log::info($request->userAgent()) at the top of the middleware, confirm the UA string, then test the detector directly in Tinker. Nine times out of ten it's a browser name mismatch (ie vs internet explorer, edge vs edg) rather than the version comparison.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource