Every public route emits a per-page SEO block — title, description, canonical URL, Open Graph card, Twitter card, and JSON-LD structured data — without any per-page work in the React layer. Buyers running a white-label install inherit the same machinery; replace the brand name in /settings/branding and the meta block follows.

What ships out of the box

Adding SEO to a new route

Two places to touch when adding a new public page:

  1. Register a default in SeoMeta::ROUTE_DEFAULTS with title / description / path. Use {brand} for any place the install's name should appear.
  2. In the controller, pass the SEO payload to Inertia:
    return Inertia::render('your/page', [
        // ... your props
        'seo' => SeoMeta::for('your-route-key'),
    ]);

The Inertia root layout (resources/views/app.blade.php) reads props.seo and emits the meta block automatically. No per-page Blade work needed.

Need to vary the description per row (e.g. a per-version changelog page)? Pass overrides:

'seo' => SeoMeta::for('changelog.show', [
    'title' => "v1.1.0 — what's new in {brand}",
    'description' => "Released " . $entry->released_at_human . " — " . $entry->summary,
])

Customising the Open Graph image

Drop a 1200×630 PNG (or JPG) at public/og-image.png. Buyers running their own install can swap the file directly via SFTP or the deploy host's file manager. The SeoMeta resolver checks the file exists at render time; if it doesn't, the meta block silently omits og:image and twitter:image rather than pointing at a 404.

Sitemap cache

The sitemap is cached for 1 hour under seo:sitemap.xml. To force a refresh after publishing a long-awaited changelog entry:

php artisan tinker --execute 'cache()->forget("seo:sitemap.xml");'

Crawler traffic on a busy install would otherwise be O(crawl rate) database hits; the cache flattens it to one rebuild per hour per node.

Excluded routes

Route prefixWhy excluded
/admin/* Platform-admin surface. Tenant-aware data.
/app/* Workspace dashboard. Auth-required, workspace-scoped.
/api/* API surface — JSON, not for crawlers.
/login, /register, password reset Auth flows — no SEO value, nothing for a crawler to index.
/settings/* Auth-required user / platform settings.