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.
Architecture fit
vlados/laravel-blade-crawler-detect is a narrow, single-purpose Blade extension: it registers @user / @unlessuser directives so templates can branch on whether the current request looks like a crawler. Architecturally it sits in the presentation layer only — no schema, no queues, no external services — which makes it cheap to evaluate and cheap to reverse. The v0.5.0 release materially improves fit for modern Laravel estates: Laravel 10–13 support, real coverage for the directive matrix (browser, Googlebot, Chrome-Lighthouse, missing UA, oversized UA, @unlessuser), and Octane/Swoole correctness. The Octane fix is significant: the UA is now read from the current request inside the directive closure rather than captured at boot, and CrawlerDetect is container-bound (transient) instead of a singleton. For any team running long-lived workers, v0.4.0 was functionally broken — every request after the first saw the boot-time UA, which would have produced silently wrong bot classification across an entire worker lifetime. That class of bug is exactly the kind that survives staging and surfaces as an unfalsifiable "SEO looks off" complaint, so the fix plus the added tests raise confidence considerably.
Integration feasibility
Low effort. Composer install, provider auto-discovery, use the directives in Blade. The breaking change (dropping Laravel 9) is a non-event for most orgs — Laravel 9 has been EOL since Feb 2024 and should already be off the supported matrix. Removal of spatie/invade reduces the dependency surface, which is a small supply-chain win. orchestra/testbench and Pest v2/v3/v4 as dev dependencies do not affect production installs. The main integration friction is behavioural, not mechanical: any application code that resolves CrawlerDetect from the container and mutates or memoizes state on it will now get a fresh instance per resolution. The 'CrawlerDetect' string alias preserves backwards compatibility for lookups, but not for identity assumptions.
Technical risk Three risks matter to a TPM.
Chrome-Lighthouse UA. Serving materially different markup to Lighthouse than to users is, at minimum, adjacent to metric gaming, and serving different markup to Googlebot than to users is the textbook definition of cloaking. This is the single most important thing to get a written position on before adoption. The defensible use is parity-preserving optimisation — same content, same links, lighter assets — not content substitution.^0.5 to be safe.The ReDoS clamp (2048-char UA before regex evaluation) is a genuine hardening item — the crawler regex is user-input-driven, and unbounded UA length against a large alternation pattern is a plausible CPU-exhaustion vector. Pinned Action SHAs and least-privilege workflow tokens are good hygiene signals for a package you'll pull into CI-adjacent tooling.
Key questions
CrawlerDetect and assume singleton identity or persisted state?Stack fit
Requires Laravel 10, 11, 12, or 13 — verify the PHP floor that Laravel 13 implies against our runtime images before scheduling. Blade directive registration is provider-based and auto-discovered, so no manual config/app.php edits. Fits cleanly alongside existing view-layer tooling (Blade components, view composers) with no interference.
Migration path
For teams already on v0.4.x: bump the constraint, run composer update vlados/laravel-blade-crawler-detect, and restart all long-running workers. The worker restart is mandatory — Octane/Swoole holds the old boot-time UA in memory, so a deploy without a worker recycle leaves the bug in place while Composer reports the new version. For teams new to the package: install, add directives to a single low-risk template, and observe.
Compatibility
'CrawlerDetect' string alias keeps container lookups working; class-identity assumptions do not survive. Grep for CrawlerDetect across app/ before merging.spatie/invade is transparent unless our own code depended on it transitively (it shouldn't have).Sequencing
CrawlerDetect container usage; remediate identity assumptions.Maintenance
Small, single-maintainer package with a low but real release cadence. Positive signals: Dependabot, SHA-pinned Actions, scoped workflow tokens, and a changelog that clearly separates Breaking / Added / Changed / Removed / Security. Negative signal: it is 0.x, so the maintenance burden is "read every changelog" rather than "trust the range." Total ongoing cost is low — a handful of lines in composer.json and a periodic review. The dev-dependency additions do not add production maintenance surface.
Support
No vendor, no SLA, community issue tracker only. Acceptable because the blast radius is confined to Blade output branching and the package is trivially removable — worst case, delete the directives and inline a str_contains check on the UA. Do not let it become load-bearing for anything that can't be deleted in an afternoon.
Scaling
Negligible. Cost is one request-header read plus a regex evaluation per directive render. Two scaling notes: detection is not memoized, so a layout using @user in several places re-runs the regex each time — fine at a handful of calls, worth a per-request cache if it spreads. And the 2048-char clamp bounds worst-case regex input, which matters for anything internet-facing at volume; prior to v0.5.0 this was an unbounded user-controlled string feeding a large alternation.
Failure modes
CrawlerDetect reference from a singleton-scoped constructor still captures state; the package fix doesn't reach into consumer code.Ramp-up Very low. Two Blade directives with obvious semantics; any Laravel developer is productive in under an hour. The real ramp-up is organisational, not technical: SEO/content stakeholders need to agree on what may differ between branches before the first template ships. Budget more time for that conversation than for the integration.
How can I help you explore Laravel packages today?