spatie/laravel-sitemap
Generate XML sitemaps for Laravel by crawling your site or building them manually. Add extra URLs, set last-modified dates, and include models via a simple interface. Write sitemaps to disk with a fluent, developer-friendly API.
Installation:
composer require spatie/laravel-sitemap
The package auto-registers.
First Crawl:
use Spatie\Sitemap\SitemapGenerator;
SitemapGenerator::create('https://your-site.com')
->writeToFile(public_path('sitemap.xml'));
First Manual Sitemap:
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
Sitemap::create()
->add(Url::create('/home'))
->writeToFile(public_path('sitemap.xml'));
php artisan vendor:publish --provider="Spatie\Sitemap\SitemapServiceProvider" --tag=sitemap-configSitemapGenerator::create('https://your-site.com')
->getSitemap()
->add(Url::create('/special-page')->setLastModificationDate(Carbon::yesterday()))
->writeToFile(public_path('sitemap.xml'));
// In your model (e.g., Post.php)
use Spatie\Sitemap\Contracts\Sitemapable;
class Post implements Sitemapable {
public function toSitemapTag(): Url {
return Url::create(route('posts.show', $this))
->setLastModificationDate($this->updated_at);
}
}
// Generate via crawler or manually
SitemapGenerator::create('https://your-site.com')
->getSitemap()
->add(Url::createFromModel($post))
->writeToFile(public_path('sitemap.xml'));
// app/Console/Commands/GenerateSitemap.php
SitemapGenerator::create(config('app.url'))
->writeToFile(public_path('sitemap.xml'));
// routes/console.php
Schedule::command('sitemap:generate')->daily();
use Spatie\Sitemap\SitemapIndex;
SitemapIndex::create()
->add('/posts_sitemap.xml')
->add('/pages_sitemap.xml')
->writeToDisk('public', 'sitemap-index.xml');
SitemapGenerator::create('https://your-site.com')
->hasCrawled(function (Url $url) {
return ! str_contains($url->getAbsoluteUrl(), '/admin');
})
->writeToFile(public_path('sitemap.xml'));
SitemapGenerator::create('https://your-site.com')
->getSitemap()
->writeToDisk('s3', 'sitemap.xml', true); // Public visibility
Crawl Depth Limits:
null (unlimited). Set explicitly to avoid infinite loops:
->configureCrawler(fn($crawler) => $crawler->depth(3))
JavaScript Execution Overhead:
spatie/browsershot and Chrome. Disable if unused (execute_javascript: false in config).Robots.txt Respect:
robots.txt by default. Use ignoreRobots() to bypass:
->configureCrawler(fn($crawler) => $crawler->ignoreRobots())
Concurrency Issues:
10. Reduce for rate-limited sites:
->setConcurrency(2)
URL Duplication:
hasCrawled to deduplicate:
->hasCrawled(fn(Url $url) => ! collect($existingUrls)->contains($url->getAbsoluteUrl()))
Model Changes Not Reflected:
Log Crawled URLs:
->hasCrawled(function (Url $url) {
\Log::info('Crawled:', ['url' => $url->getAbsoluteUrl()]);
return $url;
})
Check Crawler Profile:
Spatie\Sitemap\Crawler\Profile to customize behavior (e.g., shouldCrawl logic).Validate XML Output:
Test Locally:
GuzzleHttp\HandlerStack for offline testing:
$stack = HandlerStack::create();
$stack->push(Middleware::tap(fn($request) => \Log::info('Request:', $request->getUri())));
SitemapGenerator::create('https://your-site.com')->setGuzzleClient(new Client(['handler' => $stack]));
Custom Crawl Profiles:
// app/CustomProfile.php
use Spatie\Sitemap\Crawler\Profile;
class CustomProfile extends Profile {
public function shouldCrawl(string $url): bool {
return ! str_contains($url, '/private');
}
}
// In config/sitemap.php
'crawl_profile' => \App\CustomProfile::class,
Custom URL Tags:
Spatie\Sitemap\Tags\Url or create new tags (e.g., for video sitemaps).Pre/Post-Crawl Hooks:
SitemapGenerator::create('https://your-site.com')
->beforeCrawl(fn() => \Log::info('Starting crawl...'))
->afterCrawl(fn() => \Log::info('Crawl complete'))
->writeToFile(public_path('sitemap.xml'));
Dynamic Sitemap Routes:
// routes/web.php
Route::get('/sitemap.xml', function() {
return SitemapGenerator::create('https://your-site.com')
->getSitemap()
->toXml();
});
Cache Crawled URLs:
->setCachePath(storage_path('crawled_urls.cache'))
Limit Crawl Count:
->setMaximumCrawlCount(1000) // Avoid over-crawling
Use Disk Storage:
->writeToDisk('public', 'sitemap.xml')
Parallelize Sitemap Generation:
/posts, /pages) in parallel using Laravel queues.How can I help you explore Laravel packages today?