nmure/crawler-detect-bundle
Symfony bundle integrating CrawlerDetect to identify bots, spiders, and crawlers from user-agent strings. Provides a ready-to-use detector service and simple configuration for recognizing automated traffic in your app.
Architecture fit — Low as packaged, high in substance. This is a Symfony bundle, not a Laravel package: it ships a Bundle subclass and a DI Extension that register CrawlerDetect as a container service under Symfony's kernel/container conventions. Laravel shares Symfony components but has no bundle-registration mechanism, so none of the bundle's actual code — the CrawlerDetectBundle.php, DependencyInjection/, service aliasing — executes in a Laravel app. The value is almost entirely in the upstream dependency, jaybizzle/crawler-detect, which is framework-agnostic plain PHP: instantiate CrawlerDetect, call isCrawler($userAgent). For Laravel the correct architecture is the library plus a ~30-line service provider, not this bundle.
Integration feasibility — Straightforward once decoupled, blocked if taken literally. The bundle's last release is December 2017, which places its composer.json constraints in the Symfony 2.7–3.x / crawler-detect 1.x era. A modern Laravel app on PHP 8.1+ with Symfony 6/7 components will fail dependency resolution outright; working around it with --ignore-platform-reqs or constraint overrides buys a bundle that contributes nothing at runtime. Direct installation of jaybizzle/crawler-detect (v2.x, PHP 7.1+, PHP 8 compatible) is clean, single-dependency, and takes under an hour including tests.
Technical risk — Four concrete items. (1) Abandonment: seven-plus years without a release, zero dependents, 25 stars — no community canary for breakage. (2) Detection staleness: accuracy is a function of the regex corpus and the UA strings it knows; the upstream library is actively maintained but this bundle will pin you to whatever version its 2017 constraints permit, so the bundle is an accuracy liability even if the library is not. (3) Security posture: naive UA matching is trivially spoofed. If the intent is to block rather than classify, UA-only detection is a weak control — real bots forge Googlebot strings, and attackers can send Googlebot to probe hidden paths. Enforcement decisions need IP/rDNS verification, not this library alone. (4) False positives: the corpus is broad; legitimate monitoring, preview, and link-unfurl agents (Slack, Teams, LinkedIn, uptime checks) routinely match, and blocking them silently breaks sharing cards and alerting.
Key questions — Is the requirement classification (tag traffic in analytics/logs) or enforcement (block/rate-limit/challenge)? Enforcement raises the verification bar and changes the design. Which consumers need the result — middleware, Blade, jobs, log processors? That determines whether you want a singleton, a facade, or a middleware. Do we have a measured false-positive tolerance, and a way to audit what was blocked? Are we willing to own an unpinned upstream dependency whose corpus updates on the vendor's schedule? And is there any reason to prefer a bundle over a five-line provider — if the answer is "Symfony parity with an existing app," that's a different codebase and this package is irrelevant to it.
Stack fit — Marginal-to-poor as a direct install; good as a pattern. The bundle targets Symfony full-stack. Laravel's equivalents — service providers, facades, middleware, config publishing — map one-to-one onto what the bundle does, so the port is mechanical rather than exploratory. Nothing here is Laravel-native, and nothing here is Laravel-hostile either; it's simply a wrapper you'd rewrite in fifteen minutes.
Migration path — Do not migrate from this bundle; migrate to the library. Require jaybizzle/crawler-detect directly, register it as a singleton in AppServiceProvider (or a dedicated CrawlerDetectServiceProvider), publish a thin config/crawler-detect.php for allow/deny lists so operators can adjust behavior without code changes, and wrap access behind a facade or a typed helper. If a Symfony service definition is genuinely needed — for example, one shared library consumed by both a Symfony and a Laravel app — the right move is a small internal package with framework-agnostic core plus per-framework adapters, not a 2017 third-party bundle.
Compatibility — Core library is PHP 8-compatible and has no framework coupling; the bundle's constraints are the only blocker. Verify two things before committing: that the resolved crawler-detect version supports the PHP runtime you're on, and that the regex corpus version is recent enough for your threat/traffic profile. Both are answered by composer show and a quick evaluation against a UA sample from your own access logs — not by the bundle's README.
Sequencing — Phase 1: install the library, log-only classification in a middleware or log processor, no enforcement. Run for two to four weeks against production traffic. Phase 2: review the classification output with analytics and support; build an explicit allowlist from the false positives you observe (preview bots, uptime monitors, accessibility tools). Phase 3: only if the requirement is enforcement, add blocking/rate-limiting behind a feature flag with an observable audit channel. Phase 4: consider caching per-UA results if volume justifies it. Do not skip Phase 1 — the cost of a bad allowlist is silently broken link previews and monitoring, which is discovered late.
Maintenance — Low ongoing effort, but the maintenance burden is subtly relocated rather than removed. You are not maintaining the bundle, but you are taking a dependency on an upstream regex corpus whose quality you cannot control and whose changes arrive on someone else's schedule. Budget a periodic review — quarterly is reasonable — of false-positive reports and upstream releases. If you vendor or pin the corpus, you also own refreshing it.
Support — Effectively zero from the package: no releases since 2017, negligible stars, no dependents means no downstream bug reports to learn from. Internal knowledge will live with whoever wrote the provider. This is acceptable for a thin wrapper; it would be disqualifying for anything stateful or security-critical. Document the decision and the fallback in the ADR.
Scaling — Not a concern. Detection is stateless and CPU-bound: a single compiled regex assembled from a large pattern set, evaluated per request in roughly sub-millisecond to low-millisecond time. At high request volume it becomes a measurable but small tax on every request if invoked unconditionally — so invoke it lazily (only on the paths that need it: logging, analytics enrichment, protected routes) and memoize per-UA within a request. No shared state, no external calls, no scaling ceiling. Horizontal scaling is unaffected.
Failure modes — Ranked by expected damage. (1) Silent over-blocking: legitimate crawlers and unfurlers denied, breaking SEO indexing or link previews with no error surfaced — mitigation is log-only rollout plus an allowlist and a metric on block rate. (2) Under-detection: novel or spoofed UAs pass, giving false confidence in a control that isn't really a control — mitigation is pairing classification with IP/rDNS verification where enforcement matters. (3) Dependency abandonment: upstream PHP incompatibility in a future runtime upgrade forces removal under time pressure — mitigation is keeping the integration behind a small internal interface so swapping to another detector is a one-file change. (4) Configuration drift: allowlists edited ad hoc in production, undocumented — mitigation is config-in-repo with review.
Ramp-up — Trivial for a mid-level Laravel engineer: the library API is one method, and the provider pattern is standard Laravel. The real ramp-up cost is not technical but analytical — someone must own interpreting detection output, tuning the allowlist, and deciding whether enforcement is justified. Estimate half a day to ship the classification path, and one to two hours per month thereafter for review. The bundle itself contributes no ramp-up because it will not be installed.
How can I help you explore Laravel packages today?