SEO & Discoverability

SEO & Discoverability

Keel exposes three discoverability endpoints at runtime: /sitemap.xml, /robots.txt, and /llms.txt. They are generated from route registration and the shared docs registry, so there is no separate static file to maintain.

Mark a public page for sitemap inclusion

Router::get() supports an optional third argument for route options. Use ['sitemap' => true] when registering a real public content page.

public function get(string $uri, callable|array $action, array $options = []): void
{
    $this->addRoute('GET', $uri, $action, $options);
}

Example route registration from routes/web.php:

$router->get('/', [WelcomeController::class, 'index'], ['sitemap' => true]);
$router->get('/docs', [DocsController::class, 'index'], ['sitemap' => true]);
$router->get('/login', [AuthController::class, 'showLogin'], ['sitemap' => true]);

Once flagged, the page is returned by Router::publicPages(), and appears in /sitemap.xml automatically with no extra file edits.

Runtime endpoints

$router->get('/sitemap.xml', [SitemapController::class, 'index']);
$router->get('/robots.txt', [RobotsController::class, 'index']);
$router->get('/llms.txt', [LlmsTxtController::class, 'index']);
  • SitemapController builds XML from APP_URL + Router::publicPages() + expanded docs slugs.
  • RobotsController builds Disallow prefixes from registered protected routes/middleware.
  • LlmsTxtController outputs docs links from the same shared docs registry used by the docs sidebar.

Important behavior notes

  • Only explicitly flagged GET routes are sitemap-eligible.
  • Parameterized docs routes are expanded to real slugs instead of emitting /docs/{slug}.
  • Protected app routes (dashboard/settings/api/admin) are excluded from public discovery output.