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 Blade Crawler Detect Laravel Package

vlados/laravel-blade-crawler-detect

Detect search engine crawlers and bots directly in your Blade templates. Simple directives let you serve crawler-specific markup, hide dynamic content, or tailor SEO output without touching controllers—keeping views clean and detection logic reusable across any Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

This package adds Blade directives that branch on whether the current request's User-Agent looks like a crawler, built on jaybizzle/crawler-detect. The 0.5.0 README reframes it as a Lighthouse/PageSpeed tool, but the mechanics are the same as always: a UA sniff at render time, exposed as @user / @unlessuser.

Requirements (v0.5.0): Laravel 10, 11, 12 or 13; PHP 8.1+ (inherited from the Laravel floor). Laravel 9 is no longer supported as of this release.

composer require vlados/laravel-blade-crawler-detect

Package discovery registers the service provider; no publish step or config file is mentioned in the release notes, so assume zero configuration until you find otherwise. CrawlerDetect is resolved from the container under that class name, with the 'CrawlerDetect' string kept as a legacy alias.

Look here first:

  1. The rewritten README's widgets table — it is now the primary sales pitch, so read it critically (see Gotchas).
  2. The @user directive test file. The 0.5.0 release added real coverage for browser, Googlebot, Chrome-Lighthouse, missing UA, oversized UA, and @unlessuser. That test file is the authoritative spec for edge-case semantics — particularly what a missing UA returns, which is the behaviour people guess wrong.
  3. composer.json for the supported framework matrix before you bump.

First use case — keep heavy third-party markup out of crawler renders:

@unlessuser
    <div id="intercom-launcher"></div>
    <script src="{{ mix('js/chat.js') }}" defer></script>
@endunlessuser

That is the safe, boring, genuinely useful application. Verify it locally with:

curl -A "Googlebot/2.1 (+http://www.google.com/bot.html)" https://yourapp.test/
curl -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ..." https://yourapp.test/

Implementation Patterns

Wrap the expensive, unwrap the essential. The directive is a render-time branch, so it works anywhere in a Blade tree — layouts, partials, components' views. Keep it at the leaf level (a widget, a player, a chat bubble) rather than around your whole page body, or you end up maintaining two templates that drift.

PHP-side detection via the container. For logic outside Blade (middleware, controllers, view composers), resolve per request:

use Jaybizzle\CrawlerDetect\CrawlerDetect;

if (app(CrawlerDetect::class)->isCrawler()) {
    // ...
}

Do not stash this in a singleton, a static, or a constructor-injected property on a long-lived object. As of 0.5.0 the binding is deliberately not a singleton precisely so $_SERVER-derived state cannot leak between requests under Octane/Swoole. Caching it yourself reintroduces the bug the release just fixed.

Middleware + view shared data, if you branch in many places:

public function handle($request, Closure $next)
{
    View::share('isCrawler', app(CrawlerDetect::class)->isCrawler());

    return $next($request);
}

If you go this route, also emit a Vary: User-Agent header. Otherwise any shared cache between you and the user — Cloudflare, Fastly, nginx proxy_cache, Laravel's own response cache — will happily serve the first variant it saw to everyone.

Progressive enhancement pairing. The idiomatic shape is: render the crawlable, meaningful content always, and gate only the enhancement behind @unlessuser. That way a false negative (a bot you don't detect) costs you bandwidth, and a false positive (a human misdetected) costs you a widget — not your content.

Testing. Feature tests should set the header explicitly; the default test client UA is not a realistic browser string and may or may not be flagged:

$this->withHeader('User-Agent', 'Mozilla/5.0 ... Chrome/126 ...')->get('/')->assertSee('chat');
$this->withHeader('User-Agent', 'Googlebot/2.1')->get('/')->assertDontSee('chat');

Add a case for the empty/missing UA too — that's the one your monitoring and health checks will hit.

Gotchas and Tips

The Octane bug is the reason to upgrade, not the Laravel 13 support. In ≤0.4.x the UA was captured once inside the @user closure at boot, so on Octane/Swoole every request after the first saw the first caller's UA. That is a silent, hard-to-reproduce correctness bug: your bot pages get served to humans, or vice versa, depending on who hit the worker first. If you run Octane on 0.4.x, this is a must-upgrade. Note the corollary: if you're pinned to Laravel 9 you cannot take this fix — either leave Octane or vendor the closure change yourself.

2048-character UA clamp. Headers are truncated before hitting the crawler regex, which is the ReDoS hardening. Consequence: you cannot distinguish crawlers by a token that sits beyond 2048 chars, and pathological-but-legitimate UAs get truncated mid-token. In practice this never matters; just don't build anything that assumes the full UA reached the detector. If you do your own str_contains($request->userAgent(), ...) checks elsewhere, apply your own ceiling — the package is not protecting those.

This is not a security control. UA strings are trivially spoofed. Never use @user to gate rate limiting, admin surfaces, bot-blocking, or anything an attacker benefits from. It is a performance/UX optimisation and nothing more. Treat a spoofed Googlebot UA as free bypass of whatever you hid.

Be careful with the Lighthouse framing. The README now sells the package as a PageSpeed win, which implies serving leaner markup specifically to the Chrome-Lighthouse UA. That is UA-based differential serving, and Google's guidance treats cloaking Lighthouse as a guidelines violation — the rendered result no longer reflects what users (and Googlebot, which renders with its own UA and version cadence) actually get. If you use this to boost a score rather than to legitimately reduce crawler load, expect the win to be illusory and the risk non-trivial. Prefer fixing the actual payload.

Cache invalidation is the classic failure mode. Three ways this bites: full-page cache keyed only on URL; a CDN that strips or normalises User-Agent; and Blade fragment caching (@cache) wrapping a @user block. Any of these can pin the crawler variant permanently for humans. Vary on UA and exclude these blocks from fragment caches.

Detection lists lag. CrawlerDetect's pattern list is only as current as the underlying library version and your composer update. New AI crawlers and new Googlebot version strings appear constantly. Don't put anything load-bearing behind detection, and don't assume "not detected" means "human."

Debugging. When a block doesn't render as expected, dump the raw UA and the verdict side by side before touching the package — nine times out of ten it's a proxy or CDN rewriting the header:

dump(request()->userAgent(), app(\Jaybizzle\CrawlerDetect\CrawlerDetect::class)->isCrawler());

curl sends curl/x.y. That's a detectable bot UA. Smoke tests with bare curl will exercise the crawler branch and make you think the directive is inverted.

spatie/invade was removed. Harmless unless something in your app relied on it transitively — a composer why spatie/invade is worth the thirty seconds if you have any reflection-heavy test helpers.

Pin deliberately. Given the Laravel 9 drop and the Octane fix landing in the same release, a ^0.5 constraint in composer.json is the right range — but check the changelog before 0.6, since this package is pre-1.0 and has already shipped one breaking framework-floor 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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
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