Search by

rankbeam / laravel-seo

Fibonoir

Laravel SEO package for metadata, canonical URLs, Open Graph, linked JSON-LD, XML sitemaps and crawler controls - free and MIT licensed.

v3.21.1 2026-09-26 14:19 UTC

This package is auto-updated.

Last update: 2026-09-27 17:18:11 UTC


README

Tests Latest Version on Packagist License: MIT

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.

Built for multilingual sites. Store SEO metadata per content locale, render hreflang and inLanguage, and use title and description budgets suited to the content's script. Package messages ship in 17 languages. Your application supplies the translated content; Core does not translate ordinary model attributes automatically. See multilingual content, interface translations, or the free Filament editor's language tabs.

Upgrading from fibonoir/laravel-seo v1? 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+; PHP 8.5 requires Laravel 12 or 13)
  • 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:

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.5 / Laravel 11–13 combinations; Laravel 13 requires PHP 8.3 or newer, and PHP 8.5 is tested with Laravel 12 and 13.

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.