lbonnet/link-checker-bundle
lbonnet/link-checker-bundle helps you find and fix broken links in your PHP application. Crawl pages, validate internal and external URLs, and get clear reports on failures, redirects, and timeouts—so you can keep your site healthy and your users happy.
Architecture fit. Link checking is an asynchronous, I/O-bound batch concern, so the fit with a Laravel codebase depends on the package delegating to Laravel's queue, HTTP client, and cache rather than shipping its own transport and scheduler. Based on the single release note available, the package is a crawler/link-validator with at least a robots.txt-aware fetch path — a reasonable shape for a queued job-based integration. The naming (link-checker-bundle) is Symfony-derived, which is the single largest architectural question: "bundle" implies a Symfony DI extension, and a Symfony bundle is not directly consumable by Laravel. It may be a portable core with framework-agnostic wrappers, or the Laravel framing in this review may be imprecise. This must be resolved before any design work proceeds.
Integration feasibility. Mechanically low-risk: a Composer dependency, a published config file, and a queue worker. The real feasibility question is not "can we install it" but "does it plug into Illuminate\Queue, Illuminate\Support\Facades\Http, and Laravel's config/container, or does it drag in Symfony components that duplicate what the framework already provides." At v0.4.0 there is no stability contract, so integration is feasible for a spike or internal tooling, not for a committed roadmap dependency.
Technical risk.
Key questions.
config/link-checker.php) or a Symfony bundle requiring a bridge/adapter?Retry-After, Crawl-delay, and per-host concurrency limits?Stack fit. Assume a standard Laravel app on PHP 8.2+ with Redis-backed queues. The package should be treated as an outbound HTTP client with a scheduling wrapper: our scheduler enqueues a job, the job dispatches per-URL checks, results land in our own storage. Nothing about link checking should live inside the request/response cycle. If the package insists on synchronous checks or its own storage layer, that is a mismatch and pushes toward the build-vs-buy line.
Migration path. There is no incumbent to migrate from, so this is greenfield adoption rather than a migration. The correct path is a bounded proof of value: install in a non-production branch, point it at a staging site with a known set of broken links (including one host returning 5xx on robots.txt) to validate both detection accuracy and the new error-reporting path, and only then promote.
Compatibility. Verify PHP/Laravel constraints against our current runtime before committing; at v0.x, pin an exact version rather than a caret range, and commit the lockfile. Isolate the package behind a thin internal LinkChecker interface with our own DTOs for results, so a future replacement (or a fork) does not ripple through the codebase. Do not let package-specific types appear in our domain layer.
Sequencing.
Maintenance. A v0.x dependency with a single active contributor implies we should budget for the possibility of owning a fork. Mitigate by keeping the integration surface small and the internal interface stable. Track upstream releases manually — do not rely on automated minor-version bumps for a pre-1.0 package.
Support. No vendor support; the support channel is GitHub issues. For an internal tool this is acceptable. For anything customer-facing or SLA-bearing, it is not, unless we accept fork ownership. Document that escalation path before rollout.
Scaling. Link checking scales with URL count times host count, and the binding constraint is outbound politeness, not CPU. The package must support per-host concurrency limits; without them, a large crawl will trip rate limiters and WAFs and generate a wave of false 4xx/5xx results that look like broken links. Queue-based dispatch is mandatory — run checks in a dedicated queue with its own worker pool so a large crawl cannot starve application jobs. Cache robots.txt per host with a TTL to avoid re-fetching per URL.
Failure modes.
Ramp-up. Low for a Laravel engineer — hours to first successful run. The non-obvious learning is operational, not API: politeness limits, the fail-open/closed policy, and interpreting skipped-vs-clean results. Write a short runbook covering how to read a crawl report, what a robots.txt skip looks like, and how to re-run a single host. Given the thin evidence base and pre-1.0 status, scope initial adoption to internal tooling and revisit this assessment once upstream publishes a stable API or the contributor base broadens.
How can I help you explore Laravel packages today?