rankbeam / laravel-seo
Laravel SEO package for metadata, canonical URLs, Open Graph, linked JSON-LD, XML sitemaps and crawler controls - free and MIT licensed.
Requires
- php: ^8.2
- illuminate/cache: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/filesystem: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/routing: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^2.0|^3.0
- spatie/browsershot: ^4.0|^5.0
- spatie/laravel-sitemap: ^7.0|^8.0
Suggests
- spatie/browsershot: Generate 1200x630 OG images with a real browser (config seo.og_image)
- spatie/laravel-sitemap: Generate XML sitemaps for your models (^7.0|^8.0)
Provides
None
Conflicts
None
Replaces
None
- dev-master / 3.x-dev
- v3.20.0
- v3.19.0
- v3.18.0
- v3.17.0
- v3.16.3
- v3.16.2
- v3.16.1
- v3.16.0
- v3.15.0
- v3.14.0
- v3.13.0
- v3.12.0
- v3.11.2
- v3.11.1
- v3.11.0
- v3.10.0
- v3.9.0
- v3.8.1
- v3.8.0
- v3.7.0
- v3.6.2
- v3.6.1
- v3.6.0
- v3.5.0
- v3.4.0
- v3.3.0
- v3.2.0
- v3.1.1
- v3.1.0
- v3.0.0
- v2.0.1
- v2.0.0
- dev-fix/docs-portuguese-core
- dev-fix/docs-spanish-complete
- dev-fix/docs-spanish-core
- dev-fix/docs-french-complete
- dev-fix/docs-french-core
- dev-fix/docs-german-complete
- dev-fix/docs-german-core
- dev-fix/docs-italian-finish
- dev-fix/docs-italian-complete
- dev-fix/docs-locale-layout
- dev-docs/r13-review-status
- dev-docs/r11-portuguese-reference
- dev-docs/r11-portuguese
- dev-docs/r11-spanish-reference
- dev-docs/r11-spanish
- dev-docs/r11-french-reference
- dev-docs/r11-french
- dev-docs/r11-german-reference
- dev-docs/r11-german
- dev-docs/r11-italian-reference
- dev-docs/r11-localization
- dev-fix/r9-editor-integration
- dev-docs/r8-ai-evaluation
- dev-fix/r7-installation-floor
- dev-fix/r7-consumer-exit
- dev-fix/r7-render-evidence
- dev-fix/r6-display-locales
- dev-fix/r5-language-tag-validation
- dev-docs/r4-readability-contract
- dev-docs/r3-stemmer-contract
- dev-docs/r2-keyword-contract
- dev-fix/docs-build-postcss
- dev-fix/keyboard-code-regions
- dev-fix/docs-capture-quality
- dev-docs/pro-walkthrough
- dev-docs/design-review
- dev-fix/r1-content-locale
- dev-docs/indexnow-config-cache
This package is auto-updated.
Last update: 2026-09-10 18:01:32 UTC
README
Rankbeam is a Laravel SEO package built for production applications. The free MIT core resolves metadata through a layered precedence chain and renders canonical URLs, Open Graph and Twitter cards, a linked @id JSON-LD graph, XML sitemaps and crawler controls straight from your Laravel models and config.
composer require rankbeam/laravel-seo
Production auditing and monitoring (Pro) and the Filament admin UI ship as separate packages. This package never pulls them in.
Upgrading from
fibonoir/laravel-seov1? See UPGRADING.md — v2 renames the vendor and carves the old "full suite" down to this core; the analyzer, scanner, redirect manager, 404 monitor, and admin UI live on as separate packages (laravel-seo-filament, free;laravel-seo-pro, commercial).
What this package does
| Area | Details |
|---|---|
| Meta resolution | SEOResolver merges six layers — config → global DB defaults → model-type defaults → route defaults → computed model values → explicit seo_meta values. Null never overwrites a lower layer. |
| Computed fallbacks | Title/description/image/robots derived from model attributes. Description candidates are configurable (seo.computed.description_fields), normalized (HTML stripped, entities decoded), and truncated at a word boundary (default 160 chars, no ellipsis). Per-model robots/noindex is built in — a getSEORobots() hook or an is_indexable attribute, overridable per page via saveSEO(['robots' => …]). |
| Rendering | TagRenderer outputs HTML (@seo Blade directives), structured arrays (Vue/React), or Inertia Head format. JSON-LD is emitted with JSON_HEX_* escaping so </script> in content cannot break out of the script element. |
| Canonical policy | Derived canonicals (model URL / current URL) get the query string stripped; explicitly set canonicals are preserved verbatim. |
| Schema (JSON-LD) | Builders for Article, Breadcrumb, FAQ, LocalBusiness, Organization, Product; SchemaGraph for Organization/WebSite/WebPage nodes cross-linked via stable @ids; breadcrumbs from a page's ancestor chain with a loop guard. |
| Sitemaps | SitemapBuilder (wraps spatie/laravel-sitemap) with config-driven model sources, programmatic named sources via SEO::sitemaps()->register(...), sitemap index support, seo:sitemap command, and /sitemap.xml routes that can be disabled. Sitemaps open as a readable, branded page in the browser via a styled XSL stylesheet (on by default; search engines ignore it). |
| llms.txt | seo:llms-txt writes an optional markdown llms.txt index from the same sources as the sitemap (the registry + seo.sitemap.models), so the two never disagree. It's a compatibility file for tools that choose to consume it — Google Search does not use it. Served at /llms.txt, gated by config. |
| AI crawler control | seo:robots-txt renders a managed robots.txt (and ai.txt) from a doc-verified catalog of AI crawlers tagged by purpose — allow the AI-search and assistant crawlers (ai_search/ai_assistant), disallow the ones that train on you (ai_training) by default. SEO::robotsTxt()->aiDirectives() for a paste-able block; SEO::aiCrawlers() for the catalog + policy. Bots that ignore robots.txt are flagged advisory. |
| Markdown for bots | Content negotiation that serves clean markdown to AI crawlers instead of HTML — on Accept: text/markdown, ?format=md, or (opt-in) a known AI crawler — from a model's toSeoMarkdown(), a SEO::markdown()->register() source, or a built title+description+content fallback. Off by default; never touches a normal visitor's response. |
| Indexing guard | Ties indexability to the Laravel environment: outside seo.indexing_guard.allowed_environments (default ['production']) every page is forced to noindex,nofollow (above the whole precedence chain — overrides even a stored per-page value), robots.txt/ai.txt go disallow-all, and seo:audit warns. Stops a staging/local copy leaking into the index. Off by default, one-line opt-in SEO_INDEXING_GUARD=true, inert on production. See Indexing guard. |
| Generated OG images | Optional 1200×630 Open Graph images rendered by a real headless browser (spatie/browsershot) — correct multi-line wrapping, CJK/accents and truncation. Three publishable templates (default / article / product, selectable per model) + bundled OFL font; seo:og-images pre-generates and caches each card (content-hashed); the resolver serves it as a computed og:image fallback (existence-gated — never renders on a web request). Off by default, browser is a suggested dependency, static pre-generation only. See Generated OG images. |
| Warnings | SEOWarningEvaluator for admin UIs: title / description over the script's budget (60/160 for Latin, ~30/80 for CJK — see Multilingual content), manual-vs-fallback indicators, social-image dimension checks (min 200x200, ideal 1200x630, local files only). |
| Free audit | seo:audit — an in-process "what's wrong with my SEO right now" command (no queue, license, or network). Runs the metadata-class checks (missing / over- / under-length title & description, OG image, robots conflicts, canonical format/cross-domain/shared/insecure, hreflang code/duplicate/self-reference, focus keyword) and prints a per-page pass/warn/fail table with an explicit capability matrix. --strict for CI, --json for tooling. No numerical score (that's Pro). |
| Multilingual content | The package understands the language of your content, not only yours: per-script title/description budgets (LengthPolicy, graphemes not bytes), grapheme-safe truncation that cuts CJK at a sentence mark, locale-aware casing (Turkish İ/ı, Greek final sigma, ß), hreflang normalisation + self/x-default policies + audit codes, inLanguage on the schema graph, Yandex/Baidu/Naver/Seznam/Sogou/360/Cốc Cốc/DuckDuckGo in the crawler catalog, site-verification tags for eight engines, per-script OG-image font stacks with a missing-font pre-flight, IDN-tolerant canonical validation. Seventeen UI languages via --tag=seo-lang. See Multilingual content. |
| Explain resolution | seo:explain "App\Models\Post" 42 — the resolver precedence trace: per field, the winning layer + value, the losing layers it overrode, and post-processing notes (title suffix, canonical strip, og:url derivation, image absolutization, indexing guard). Plus a site-level ledger (site name, default locale, canonical host) naming each value's source. Human route:list-style output + --json. Read-only; can't drift from what renders. See Explain resolution. |
| Migration importer | seo:import-from ralphjsmit — bulk-import SEO data from a competing Laravel package's storage into seo_meta. Idempotent, --dry-run, --model= scoping, explicit field mapping, morph rows re-resolved to the live model. See Migrating from other packages. |
| WordPress importer | seo:import-from wordpress-csv (a CSV export) and seo:import-from yoast / rank-math (the live WordPress DB via --connection=). Maps Yoast/Rank Math keys explicitly (incl. OG/Twitter overrides), resolves %%title%%-style template tokens, matches posts to your models by slug, and emits a redirects CSV for Pro. See Migrating from WordPress. |
Database tables: seo_meta (per-model explicit values, morph + locale) and seo_defaults (global/model-type/route defaults). Nothing else.
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13 (CI runs the full matrix; Laravel 13 requires PHP 8.3+)
spatie/laravel-sitemap^7.0 or ^8.0 (suggested, required for sitemap generation)
Installation
composer require rankbeam/laravel-seo php artisan vendor:publish --tag=seo-config php artisan migrate
Quick start
use Rankbeam\Seo\Traits\HasSEO; class Post extends Model { use HasSEO; public function getUrlForSEO(): string { return route('posts.show', $this); } }
<head> @seo($post) </head>
Explicit values from the admin side:
$post->saveSEO([ 'title' => 'Custom SEO Title', 'description' => 'Custom meta description', ]);
Headless / Inertia:
use Rankbeam\Seo\Facades\SEO; return Inertia::render('Post', [ 'seo' => SEO::forInertia($post), ]);
Sitemap sources:
// config/seo.php 'sitemap' => ['models' => [Post::class => ['priority' => 0.8]]], // or programmatically (e.g. in a service provider) SEO::sitemaps()->register('pages', fn () => ['/about', '/contact']);
Then generate the files (requires spatie/laravel-sitemap) — the package's
/sitemap.xml route serves what this command writes:
composer require spatie/laravel-sitemap php artisan seo:sitemap
Serving your own static /sitemap.xml? Disable the package routes:
// config/seo.php 'routes' => ['enabled' => false],
Audit your SEO
# Audit the models under seo.audit.models / seo.sitemap.models php artisan seo:audit # Or target specific models, CI-fail on any issue, or emit JSON php artisan seo:audit --model="App\Models\Post" --strict php artisan seo:audit --json
A free, in-process pass/warn/fail report over the metadata-class checks. The rendered-HTML and live-canonical checks, and the numerical score, are part of the Pro scan — the command prints that boundary every run.
Generate llms.txt
php artisan seo:llms-txt # writes public/llms.txt php artisan seo:llms-txt --print # print to stdout (dry run)
A markdown index of your site for AI crawlers (llms.txt),
built from the same sources as your sitemap — registered sources plus
seo.sitemap.models, with the same noindex/unpublished exclusions — so the two
never disagree. It is served at /llms.txt (disable that route via
seo.llms_txt.route), and you can schedule it alongside the sitemap:
Schedule::command('seo:llms-txt')->daily();
AI crawler control (robots.txt / ai.txt)
php artisan seo:robots-txt # writes public/robots.txt php artisan seo:robots-txt --print # print to stdout (dry run) php artisan seo:robots-txt --ai-txt # also write public/ai.txt
A managed robots.txt for the AI era, rendered from a doc-verified catalog of
AI crawlers tagged by purpose. The default policy allows the AI-search and
assistant crawlers (ai_search / ai_assistant) and disallows the ones that
train on your content (ai_training); override per purpose or per bot in
seo.ai_crawlers. Grab
just the managed block for an existing file with SEO::robotsTxt()->aiDirectives(),
or serve /robots.txt dynamically (off by default — it won't shadow a static
file). Bots documented not to honour robots.txt are flagged advisory. See
AI crawler control.
Guides
Long-form articles on the Rankbeam blog, a Laravel app that runs this package on itself:
- Laravel SEO: the complete guide
- Best Laravel SEO packages compared
- Laravel meta tags
- Canonical URLs in Laravel
- JSON-LD schema graphs
- Laravel sitemaps
- Laravel SEO audit in CI
- hreflang done right
- Answer engine optimization
- Filament SEO fields
Test status
Recorded full suite for core 3.20.0: 1036 passed (4363 assertions), 0 failed under PHP 8.4 / Laravel 13, with 17 default skips (16 opt-in Chrome image checks and one ext-xsl check). Real OG/PDF rendering and runtime-consumer checks run separately; default skips are not evidence that those paths ran. Public CI covers the supported PHP 8.2–8.4 / Laravel 11–13 combinations; Laravel 13 requires PHP 8.3 or newer.
git clone https://github.com/rankbeam/laravel-seo.git
cd laravel-seo
composer install
vendor/bin/pest
What is not in this package
Queued site scans, content analysis, redirect manager, 404 monitor, and the SEO dashboard ship in laravel-seo-pro (commercial); the Filament admin form fields ship in laravel-seo-filament (free). The old seo:install stub-publishing flow is gone.
Contributing
Translations are welcome — see TRANSLATING.md (language files, glossary, the parity test).
Bug reports, fixes and features are welcome — see CONTRIBUTING.md for the package boundary (what belongs in core vs. the Filament/Pro packages), how to run the suite, the rendering contract, and the versioning/BC policy.
Security
Please report security vulnerabilities privately, not in a public issue — see SECURITY.md.
License
MIT — see LICENSE.md.