yvkibira/laravel-sitemap-generator

Agnostic Laravel sitemap package — discovers routes, excludes by pattern, resolves dynamic parameters.

Maintainers

Package info

github.com/Yvkibira/laravel-sitemap-generator

pkg:composer/yvkibira/laravel-sitemap-generator

Transparency log

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-07-20 19:49 UTC

This package is auto-updated.

Last update: 2026-07-20 20:03:25 UTC


README

Generate SEO-optimized XML sitemaps automatically from your Laravel application's routes. Zero assumptions about your app's structure — just install, configure exclusions, and go.

Non-Technical Overview

What does it do?

It creates a sitemap.xml file that tells search engines (Google, Bing, etc.) which pages exist on your website. This helps your pages get indexed faster and appear in search results.

How does it work?

  1. The package looks at all the pages (routes) your Laravel app has registered
  2. It filters out pages you don't want in search results (like admin panels)
  3. It generates a clean XML file with all your public page URLs
  4. Search engines find this file via your robots.txt or when you submit it

What you need to do

  1. Install the package
  2. Publish the config file
  3. Tell it which pages to exclude (e.g., /admin/*)
  4. If you have dynamic pages (like blog posts with /blog/my-post), write a short class that tells the package how to find them
  5. Set up a cron job to regenerate the sitemap daily (or enable the built-in scheduler)

For Developers

Installation

composer require yvkibira/laravel-sitemap-generator
php artisan vendor:publish --tag=sitemap-config

The package auto-registers via Laravel's package discovery. It:

  • Registers GET /sitemap.xml route
  • Registers php artisan sitemap:generate command
  • Publishes config/sitemap.php when you run the vendor:publish command

Quick Start

# Generate sitemap immediately
php artisan sitemap:generate

# Output: Sitemap generated at storage/app/public/sitemap.xml
# Also written to: public/sitemap.xml

Visit /sitemap.xml in your browser to see the generated sitemap.

Configuration

Publish the config, then edit config/sitemap.php:

return [
    'exclude' => [
        'patterns' => [
            'admin/*',       // Exclude all admin routes
        ],

        'middleware' => [
            // 'auth',       // Exclude routes with auth middleware
        ],

        'route_names' => [
            // 'admin.*',   // Exclude routes named admin.*
        ],
    ],

    'dynamic_routes' => [
        // ⚠️  Parameterized routes (blog/{post}) do NOT appear
        //     unless you add them here. Nothing is auto-guessed.
        // 'blog/{post}' => true,
    ],

    'robots' => [
        'enabled' => true,
        'disallow' => [
            '/admin',
        ],
    ],

    'schedule' => [
        'enabled' => false,
        'frequency' => 'daily', // hourly, twice-daily, daily, or Laravel frequency methods
        'time' => '03:00',      // Only used when frequency is 'daily'
    ],
];

Route Exclusion

Three ways to exclude routes from the sitemap:

By URI pattern — Matches the URL path:

'exclude' => [
    'patterns' => [
        'admin/*',
        'api/*',
        'auth/*',
        '_debugbar/*',
    ],
],

The * is a wildcard that matches any segment(s). admin/* matches /admin, /admin/users, /admin/posts/create, etc.

By middleware — Excludes routes that use specific middleware:

'exclude' => [
    'middleware' => [
        'auth',
        'filament.auth',
    ],
],

By route name — Matches named routes:

'exclude' => [
    'route_names' => [
        'filament.admin.*',
        'debugbar.*',
    ],
],

You can combine all three — a route is excluded if it matches any rule.

Dynamic Routes (Parameterized URLs)

⚠️ These do NOT appear in your sitemap unless you explicitly opt in below.

Routes with parameters like /blog/{post} cannot be auto-discovered because the package doesn't know what values to substitute. You must configure each one before it appears in the sitemap. If a route is missing from your sitemap, check your dynamic_routes config.

Opt-in Convention (true)

'dynamic_routes' => [
    'blog/{post}' => true,
]

true tells the package to use convention:

  1. Extracts parameter name postApp\Models\Post
  2. Uses slug column if it exists, else getRouteKeyName()
  3. Auto-filters by is_published if that column exists
  4. Sets lastmod from updated_at

Config Override (Array)

For routes that need custom filtering:

'dynamic_routes' => [
    'cloud-servers/{package}' => [
        'where' => [
            'type' => 'virtual_machine',
            'is_product_page_published' => true,
        ],
        'priority' => '0.9',
    ],
]

Available options: model, column, where (key-value array), priority, changefreq, scope. If model or column are omitted, they're inferred from convention.

Simple Model Class String

'dynamic_routes' => [
    'blog/{post}' => \App\Models\Post::class,
]

Equivalent to a config array with just model. Convention handles the rest (column, published filter).

Custom DynamicRouteResolver

For full control, implement the interface and tag it in a service provider:

use YvKibira\LaravelSitemapGenerator\Contracts\DynamicRouteResolver;
use YvKibira\LaravelSitemapGenerator\SitemapUrl;

class BlogPostResolver implements DynamicRouteResolver
{
    public function resolve(string $uri, array $parameterNames): array
    {
        if ($uri !== 'blog/{post}') {
            return [];
        }

        return Post::where('is_published', true)
            ->get()
            ->map(fn (Post $post) => new SitemapUrl(
                url: url("blog/{$post->slug}"),
                priority: '0.7',
                changefreq: 'monthly',
                lastmod: $post->updated_at,
            ))
            ->all();
    }
}
// In a service provider
$this->app->tag(BlogPostResolver::class, 'sitemap.resolvers');

Custom resolvers take priority — they run before the convention/config resolver.

Scheduled Generation

Enable automatic daily sitemap regeneration in your config:

'schedule' => [
    'enabled' => true,
    'frequency' => 'daily',
    'time' => '03:00',
],

Supported frequencies:

  • hourly
  • twice-daily
  • daily (uses the time setting)
  • everyMinute, everyTwoMinutes, everyFiveMinutes, everyTenMinutes, everyFifteenMinutes, everyThirtyMinutes

SitemapUrl Object

new SitemapUrl(
    url: 'https://example.com/page',  // Full absolute URL
    priority: '0.8',                   // SEO priority (0.0 to 1.0)
    changefreq: 'weekly',              // How often content changes
    lastmod: Carbon::now(),            // Optional: last modified date
);

Default priority is 0.8, default changefreq is weekly.

Robots.txt

The command generates a robots.txt file alongside the sitemap with:

User-agent: *
Disallow: /admin
Allow: /

Sitemap: https://example.com/sitemap.xml

Configure the disallow paths in config/sitemap.php under robots.disallow, or disable robots.txt entirely with robots.enabled: false.

Architecture

Route::getRoutes() ──► RouteDiscovery ──► Sitemap ──► XmlRenderer ──► XML string
                           │                  │
                      exclude rules      DynamicRouteResolver[]
                      (config)           (tagged services)

Classes

Class File Purpose
SitemapUrl src/SitemapUrl.php Value object with url, priority, changefreq, lastmod
DynamicRouteResolver src/Contracts/DynamicRouteResolver.php Interface for expanding parameterized routes
RouteDiscovery src/Support/RouteDiscovery.php Reads all routes, applies exclusion filters
Sitemap src/Sitemap.php Orchestrator: discovers routes, resolves params, builds URLs
XmlRenderer src/Renderers/XmlRenderer.php Builds XML string from SitemapUrl array
GenerateSitemapCommand src/Commands/GenerateSitemapCommand.php Artisan command for file generation
SitemapController src/Http/Controllers/SitemapController.php HTTP endpoint serving sitemap.xml
SitemapServiceProvider src/SitemapServiceProvider.php Registers command, route, config, scheduler

Dependencies

  • laravel/framework (no Filament, no specific models, no packages)

The package is a Laravel package. It uses only Illuminate\Routing\Router and Illuminate\Routing\Route from the framework — no Filament, no Livewire, no models.

Testing

php artisan test --filter=SitemapPackage

The test suite includes:

  • Unit tests for route discovery, filtering, and exclusion
  • Unit tests for XML rendering
  • Mock-based tests for the controller and command
  • Integration tests with the host application's routes