Fix Your Sitemap for Laravel

Updated April 2026·By SitemapFixer Team

Laravel doesn't ship a built-in sitemap generator, so most teams use spatie/laravel-sitemap. Memory blowups from loading entire Eloquent collections, stale sitemaps, and missing multi-tenant domains are the typical failure modes.

Analyze your Laravel sitemap nowTry Sitemap Fixer Free

The standard Laravel sitemap pattern: an Artisan command runs on cron, builds a sitemap index, writes per-entity files to public/sitemaps/. When it goes wrong, it goes wrong predictably: someone loads ::all() on a 400k-row table, PHP OOMs, the cron job stops firing, and the sitemap slowly rots until somebody notices rankings have tanked.

Debugged a Laravel marketplace app last month. GenerateSitemap command had been failing silently for six weeks because a developer swapped chunkById(500) for get() "to make it simpler". The sitemap was frozen at 38k URLs while the actual catalog grew to 62k. GSC coverage dropped 18% during that window. Switching back to chunkById fixed the OOM; running once caught everything up.

Working Artisan command

// app/Console/Commands/GenerateSitemap.php
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\SitemapIndex;
use Spatie\Sitemap\Tags\Url;

public function handle(): int
{
    $index = SitemapIndex::create();
    $shardSize = 40000;

    // Static pages
    $static = Sitemap::create()
        ->add('/')
        ->add('/about')
        ->add('/pricing');
    $static->writeToFile(public_path('sitemaps/static.xml'));
    $index->add('/sitemaps/static.xml');

    // Posts, chunked to avoid OOM
    $shard = 0;
    $sitemap = Sitemap::create();
    $count = 0;

    Post::where('published', true)
        ->whereNull('deleted_at')
        ->orderBy('id')
        ->chunkById(1000, function ($posts) use (&$sitemap, &$count, &$shard, &$index, $shardSize) {
            foreach ($posts as $post) {
                $sitemap->add(
                    Url::create(route('posts.show', $post))
                        ->setLastModificationDate($post->updated_at)
                        ->setChangeFrequency(Url::CHANGE_FREQUENCY_WEEKLY)
                        ->setPriority(0.7)
                );
                if (++$count >= $shardSize) {
                    $sitemap->writeToFile(public_path("sitemaps/posts-{$shard}.xml"));
                    $index->add("/sitemaps/posts-{$shard}.xml");
                    $shard++;
                    $sitemap = Sitemap::create();
                    $count = 0;
                }
            }
        });

    if ($count > 0) {
        $sitemap->writeToFile(public_path("sitemaps/posts-{$shard}.xml"));
        $index->add("/sitemaps/posts-{$shard}.xml");
    }

    $index->writeToFile(public_path('sitemap.xml'));
    return self::SUCCESS;
}

Common Laravel Sitemap Issues

Scheduling and cron

// app/Console/Kernel.php (or routes/console.php in Laravel 11)
$schedule->command('sitemap:generate')
    ->hourly()
    ->onOneServer()
    ->withoutOverlapping()
    ->appendOutputTo(storage_path('logs/sitemap.log'));

// Verify scheduler is running
* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1

Multi-tenant sites

For stancl/tenancy or hyn/multi-tenant setups, wrap the generator in the tenant context and write the file to that tenant's public path. Don't try to share a single sitemap.xml across tenants - each tenant has different URLs, and Google treats each domain as a separate property. Cron the command once per tenant, or use a scheduled loop that iterates active tenants.

Step-by-Step Fix Guide

  1. Install composer require spatie/laravel-sitemap and publish its config
  2. Verify APP_URL in .env matches your production domain
  3. Create a GenerateSitemap command that builds an index and per-entity shards
  4. Use chunkById(1000) on any Eloquent query that might exceed 10k rows
  5. Cap shards at 40k URLs so you stay under the 50k sitemap limit with headroom
  6. Schedule hourly with ->onOneServer() and ->withoutOverlapping()
  7. Verify cron is firing: tail -f storage/logs/sitemap.log
  8. Test with curl https://yoursite.com/sitemap.xml - should return 200 and an index
  9. Submit to Google Search Console

Frequently Asked Questions

Should I use spatie/laravel-sitemap or write my own?
Use spatie/laravel-sitemap. It handles edge cases (large files, indexes, lastmod) that you'd otherwise reimplement badly. For sites over 5M URLs you might outgrow it, but that's rare.
Why am I getting out of memory when generating a Laravel sitemap?
You're probably calling ::all() or a non-chunked ->get() on a large table. Switch to ->chunkById(1000) so PHP's memory stays bounded regardless of catalog size.
How do I handle multi-tenant sitemaps in Laravel?
Run the generator per tenant via stancl/tenancy's tenancy()->runForMultiple() or equivalent. Each tenant writes its own sitemap to its public path.
Analyze your Laravel sitemap
Find all issues in your sitemap - free, no credit card needed
Analyze My Sitemap Free
Other platform guides