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 Seo Scanner Laravel Package

vormkracht10/laravel-seo-scanner

Scan any Laravel site for SEO issues with a single Artisan command. laravel-seo-scanner checks titles, meta descriptions, headings, images, internal links and more, flagging problems in a clear report so you can fix them fast and improve rankings. Configurable checks make it easy to tailor.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its config, then run your first scan against a single URL before wiring it into anything scheduled:

composer require backstagephp/laravel-seo-scanner
php artisan vendor:publish --tag="seo-scanner-config"
php artisan seo:scan --url=https://example.com

Where to look first:

  1. config/seo-scanner.php — this is the real API surface. Checks, thresholds, chunk sizes, queue connection, and the SPA/rendering options all live here. Read it top to bottom once; it will save you an hour of guessing.
  2. The package's registered checks — each check is a discrete unit with its own pass/fail logic. Understanding the check list tells you what "SEO score" actually means here, which matters when you report it to stakeholders.
  3. The migration/models if the package persists results. Most teams want scan history, not just a one-shot command output.

First use case: point it at your staging site, run a scan, and compare the failing checks against your actual templates. Nine times out of ten the first scan surfaces missing meta descriptions on paginated pages and duplicate titles on filtered listings — cheap, high-value fixes.

v5.22.0 is a good release to start on. It fixes two things that previously made the package awkward in real projects: a single failing check can no longer abort an entire scan, and throttled chunk jobs now retry instead of dying with a MaxAttempts failure. If you'd previously tried this and found scans silently incomplete, retry it.

Implementation Patterns

Chunked scanning for real sites. For anything beyond a handful of URLs, run the scan against a sitemap or URL list and let the package chunk the work across queue jobs. Make sure your queue worker has a sensible --tries and --timeout; the scanner's own retry semantics (see Gotchas) interact with these.

Schedule it, don't babysit it. A weekly scheduled scan with results persisted gives you a regression signal:

$schedule->command('seo:scan')->weekly()->withoutOverlapping();

withoutOverlapping() matters more than usual here — overlapping scans against the same site will fight over the queue and inflate your request volume.

SPA / Inertia / Livewire frontends. v5.22.0 adds JavaScript rendering with a safe fallback. Enable SPA mode in the config only for sites that genuinely need it (React/Vue/Nuxt front-ends where meta tags are injected client-side). The fallback means a rendering timeout degrades to the non-rendered HTML rather than failing the page — so you get a result, but treat results for rendered pages with slightly more suspicion and spot-check a few manually before acting on them.

Gate deployments on a subset of checks. Rather than gating on total score (noisy, moves for reasons unrelated to your change), pick three or four checks that are binary and meaningful — canonical present, title non-empty, no noindex on indexable routes — and fail CI on those only.

Custom checks. The extension point is adding your own check class and registering it in config. Use this for house rules: required OG image dimensions, mandatory structured data on product pages, brand-suffix in titles. Keep custom checks deterministic and offline; checks that hit third-party APIs are the main source of flaky scans.

Persist and diff. Store scan results per URL per run and diff run-over-run. The most useful output isn't "37 issues" — it's "these 4 URLs regressed since last Tuesday," which maps directly to a deploy.

Gotchas and Tips

Per-check isolation is new in v5.22.0 (PR #121). Before this, one throwing check — usually a malformed response or a check with a null guard missing — could abort the whole scan, leaving you with a partial report you didn't know was partial. If you have code that treated an incomplete scan as an empty one, re-check your assumptions. Also: failures are now isolated, not surfaced, so a check that throws on every page will quietly contribute nothing. Watch for checks that never appear to fail or never appear to pass.

Throttled jobs now retry (#123). Previously, a rate-limited chunk job was counted against max attempts and could fail the scan under load. If you tuned --tries or queue timeouts upward as a workaround, revisit those settings — you may now be over-provisioning retries, which turns a genuinely broken scan into a very slow one. Also align your queue worker's retry_after with the package's throttle/backoff, or you'll get duplicate execution.

MaxAttemptsExceededException vs. real failures. Post-upgrade, a scan that fails is more likely to be a genuine error than a throttle artefact. Don't reflexively bump retries when a scan fails now — read the failed job's exception first.

SPA rendering is opt-in and slower. Rendered scans are dramatically more expensive per URL (browser process per page or per pool). Budget accordingly: reduce chunk size, cap concurrency, and don't run it on the same queue as time-sensitive jobs. The "safe fallback" can mask a broken renderer — if scores for SPA pages look suspiciously close to their raw-HTML scores, the renderer probably isn't working.

Local vs. CI environments differ. External HTTP is the classic flake source. The package's own suite went hermetic in this release (#125) for exactly that reason — take the hint and fake HTTP in your own tests around scan-triggering code. For real scans, prefer running against a known stable environment rather than production, and be aware that staging behind basic auth will produce a wall of failures that look like SEO bugs.

Config cache. After editing config/seo-scanner.php, run php artisan config:clear. Silently-ignored config changes are the single most common "the package is broken" report.

Rate limiting is your responsibility at the edges. The package throttles its own chunk jobs, but if you're scanning a site you don't own, don't. And if you're scanning your own production site, schedule the scan for a low-traffic window — the scanner does not know about your capacity.

Watch the dependency bumps. The two Dependabot PRs here (actions/checkout, actions/cache) are CI-only and don't affect your app, but they're a reminder that this package iterates fast on patch versions. Pin to a minor version (^5.22) rather than floating, and read the changelog on bumps — v5.22.0 changed failure semantics in ways that could alter your reported numbers without any change to your site.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox