Skip to content

SEO & AI Search

Published pages are server-rendered with no client-side JavaScript, so every crawler — Googlebot, GPTBot, ClaudeBot, PerplexityBot — sees the full content on the first request. Nothing on this page needs configuration to work; it describes what you get by default and which knobs exist if you want them.

What's automatic

Every published page emits, with no configuration:

  • A per-page <title> and <meta name="description">
  • A self-referential <link rel="canonical">
  • Open Graph tags (og:title, og:description, og:image + og:image:alt, og:url, og:type, og:locale, and an og:locale:alternate per other language)
  • A twitter:card (large image when a share image is set) with twitter:image:alt
  • hreflang alternates for every locale a multi-locale site serves, including x-default
  • JSON-LD structured data: WebPage on every page (datePublished from the page's creation, dateModified from the published revision), plus WebSite and Organization on the homepage, BreadcrumbList for nested slugs, Product on dynamic product pages, and FAQPage from any FAQ section

Hosts that render their own dynamic pages through <x-filamentcraft::layout> (cart, checkout, account) get the site-level defaults automatically, defaulting to noindex for those utility pages.

Per-page SEO

In the editor, open the Settings cog in the topbar and choose SEO — the modal has two tabs.

The SEO modal's Search engine tab, showing the live SERP preview, character counters, indexing toggle and canonical override

Search engine shows a live Google-style preview that updates as you type, character counters against the recommended lengths (~60 for the title, ~155 for the description), a canonical URL override for the rare page that duplicates another URL, and one indexing switch:

Show this page in search engines controls the noindex robots meta and whether the page appears in the sitemap — a single toggle, so the two can never disagree.

The preview renders the real snippet — site monogram, breadcrumb path, and the truncation searchers will actually see:

A close-up of the live SERP preview inside the SEO modal

Social shows a share-card preview. By default it reuses the search title and description ("Use the search title & description for social too"); uncheck it to write dedicated Open Graph copy. Upload a per-page share image or fall back to the site default.

The Social tab, showing the 1200 × 630 share-card preview and the image uploader

All per-page SEO is stored per locale — translating a page translates its metadata too.

Redirects when a slug changes

Slugs live in Page settings, not the SEO modal — both sit under the same topbar Settings cog. Change a slug there and the form offers "Redirect the old URL to the new one" (checked by default). Accepting it creates a 301 from the old path to the new one, so existing links and search rankings keep working. Chained redirects are collapsed automatically — visitors never make two hops.

Site-wide defaults

Site settings → Search & AI sets the defaults every page inherits:

  • Whether to append the site name to page titles
  • A default meta description and social share image
  • An organisation logo and social profile URLs (these become sameAs links in the Organization structured data — the entity signals AI engines weigh heavily)
  • A site-wide "Allow search engines to index this site" switch — turn it off for staging to add a blanket noindex

AI crawlers & GEO

The AI crawler policy decides which AI bots may access the site. The presets are built around one distinction: search/answer bots and training bots are separate levers.

  • Answer bots (OAI-SearchBot, ChatGPT-User, PerplexityBot, Claude-SearchBot, and Google's own Googlebot, which powers AI Overviews) are what put you in AI answers. FilamentCraft never blocks these — blocking them just removes you from AI search.
  • Training bots (GPTBot, ClaudeBot, Google-Extended, CCBot, …) feed model training. Blocking them never removes existing citations, so they are the only bots a policy toggles.
PresetEffect
Maximum AI visibility (default)Everything allowed except Bytespider (which ignores robots.txt)
Protect content from trainingTraining bots blocked; answer bots still allowed
Custom per-bot rulesTick exactly which training crawlers may access the site

This renders into robots.txt for you. Two more toggles live here: Publish llms.txt and Submit new pages to IndexNow.

The AI crawlers & GEO modal, showing the crawler policy select plus the llms.txt and IndexNow toggles

Public files

When public routing is enabled, FilamentCraft serves these per site:

PathWhat it is
/sitemap.xmlPublished, indexable pages with honest lastmod and inline hreflang alternates. No priority/changefreq (Google ignores them). With a custom locale URL resolver the inline alternates are omitted (the resolver is request-bound) — the per-page hreflang tags carry the correct URLs instead.
/robots.txtThe AI-crawler policy plus a Sitemap: line
/llms.txtAn llmstxt.org index of your pages
/{key}.txtThe IndexNow verification key

Delete the stock public/robots.txt

A fresh Laravel app ships a static public/robots.txt. Your web server serves that file before the request ever reaches Laravel, so it silently shadows FilamentCraft's dynamic /robots.txt (and a stray public/sitemap.xml would shadow the sitemap the same way). Delete those static files so the per-site, AI-crawler-aware versions take effect.

Each can be turned off in config:

php
// config/filamentcraft.php
'seo' => [
    'sitemap' => true,
    'robots_txt' => true,
    'llms_txt' => true,
    'indexnow' => ['enabled' => true, 'endpoint' => 'https://api.indexnow.org/indexnow'],
],

IndexNow submits each page to Bing, Yandex, Naver and Seznam the moment it's published — and Bing's index feeds ChatGPT Search and Copilot. Google does not consume IndexNow, so it complements the sitemap rather than replacing it. It's fire-and-forget: it never blocks a publish and skips non-public hosts (a local publish is a no-op).

An honest note on llms.txt

As of 2026 no major AI provider has committed to reading llms.txt, and most files in the wild get zero AI requests. It ships because it is cheap and harmless — not because it is a proven ranking lever. Your server-rendered HTML is what AI crawlers actually read.

If public routing is off, the host app owns these paths — FilamentCraft won't register them.

Extending it

  • Add dynamic URLs to the sitemap (products, articles) with a closure:

    php
    use FilamentCraft\Seo\SitemapBuilder;
    
    SitemapBuilder::appendUsing(fn ($site) => Product::query()
        ->get()
        ->map(fn ($p) => ['loc' => route('products.show', $p), 'lastmod' => $p->updated_at->toAtomString()]));
  • Override or add head tags by pushing to the fc-head stack from any page that wraps the layout — your pushes render after FilamentCraft's tags, so they win.

  • Point canonicals at your own routing by registering a PublicUrlResolver closure — the canonical URL, og:url, and sitemap entries all follow it.

Diagnostics

php artisan filamentcraft:doctor audits SEO too: pages missing a description, duplicate titles, missing default share images, staging sites set to noindex, and whether the public SEO files are being served.