
September 07, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Search engines cannot rank pages they never discover, and on a dynamic Laravel application that gap shows up fast when routes, slugs, and published content change weekly. Laravel sitemap generator SEO best practices turn your route model, database records, and Laravel SEO foundations into a machine-readable XML sitemap that stays current without manual edits. Whether you run a legal-tech portal, a booking system, or a multi-vendor marketplace, the goal is the same: expose canonical, indexable URLs and keep junk out of Google's crawl budget. This guide covers package choice, URL inclusion rules, performance, submission, and deployment automation for production Laravel 12 and 13 applications on PHP 8.3+.
/sitemap.xml, linking it in robots.txt, and regenerating on deploy or content change.How do you generate a sitemap in Laravel for SEO?
A Laravel sitemap is not a Blade view you hand-edit. It is generated output that conforms to the Sitemaps XML protocol and lists URLs search engines should crawl. In practice you wire a generator command into your application lifecycle so the file reflects live data.
Install and configure Spatie Laravel Sitemap
On real client projects I reach for spatie/laravel-sitemap because it integrates cleanly with Eloquent, supports sitemap indexes for large sites, and publishes a dedicated Artisan command. Install with Composer 2.10:
composer require spatie/laravel-sitemap
php artisan vendor:publish --provider="Spatie\Sitemap\SitemapServiceProvider" Create a generator class that assembles URLs from static routes and database-backed content:
<?php
namespace App\Console\Commands;
use App\Models\Post;
use App\Models\Service;
use Illuminate\Console\Command;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
class GenerateSitemap extends Command
{
protected $signature = 'sitemap:generate';
protected $description = 'Build sitemap.xml for SEO';
public function handle(): int
{
$sitemap = Sitemap::create()
->add(Url::create(route('home'))
->setPriority(1.0)
->setChangeFrequency(Url::CHANGE_FREQUENCY_WEEKLY))
->add(Url::create(route('about'))
->setPriority(0.6));
Post::query()
->published()
->select(['slug', 'updated_at'])
->orderByDesc('updated_at')
->chunkById(500, function ($posts) use ($sitemap) {
foreach ($posts as $post) {
$sitemap->add(
Url::create(route('blog.show', $post->slug))
->setLastModificationDate($post->updated_at)
->setChangeFrequency(Url::CHANGE_FREQUENCY_MONTHLY)
->setPriority(0.8)
);
}
});
Service::query()
->where('is_indexable', true)
->each(fn ($service) => $sitemap->add(
Url::create(route('services.show', $service->slug))
->setLastModificationDate($service->updated_at)
));
$sitemap->writeToFile(public_path('sitemap.xml'));
return self::SUCCESS;
}
} Register the command in routes/console.php (Laravel 11+) or your scheduler so it runs nightly and after bulk imports:
use Illuminate\Support\Facades\Schedule;
Schedule::command('sitemap:generate')
->dailyAt('02:15')
->withoutOverlapping()
->onOneServer(); Expose the sitemap at a stable URL
Serve the generated file from public/sitemap.xml or stream it through a route only if you need dynamic headers. Static files behind your web server are faster and simpler for page-speed work. Add a route fallback only when generation is fully dynamic:
Route::get('/sitemap.xml', function () {
abort_unless(file_exists(public_path('sitemap.xml')), 404);
return response()->file(public_path('sitemap.xml'), [
'Content-Type' => 'application/xml; charset=UTF-8',
]);
}); Which Laravel sitemap package should you use in 2026?
You can build XML by hand, but maintained packages reduce bugs around escaping, lastmod formatting, and sitemap index splitting. Compare the two approaches teams still use on PHP 8.3+ Laravel apps:
| Option | Best for | Pros | Cons |
|---|---|---|---|
| spatie/laravel-sitemap | Most Laravel 12/13 apps | Active maintenance, chunk-friendly, sitemap index support, video/image tags | Requires scheduled generation for large catalogs |
| Custom XML builder | Tiny static sites | Zero dependency, full control | Easy to get charset, escaping, or 50k URL limits wrong |
| CMS plugins (WordPress) | WordPress/WooCommerce stacks | Automatic on publish | Not applicable to custom Laravel routing logic |
For Laravel 13.x on PHP 8.3 or PHP 8.5, Spatie remains the pragmatic default I've used alongside other Spatie packages such as sluggable and media library on production apps. If your site exceeds 50,000 URLs or a 50 MB uncompressed file, split into a sitemap index:
use Spatie\Sitemap\SitemapIndex;
use Spatie\Sitemap\Sitemap;
SitemapIndex::create()
->add('/sitemaps/pages.xml')
->add('/sitemaps/blog.xml')
->add('/sitemaps/products.xml')
->writeToFile(public_path('sitemap.xml')); E-commerce builds like Nepal Gift Card and directory platforms such as Lawyers Pokhara benefit from split sitemaps so product or listing regeneration does not block the entire crawl signal. Pair this with SEO-optimized Laravel eCommerce patterns when faceted navigation creates many thin URLs.
What URLs should a Laravel sitemap include and exclude?
Including every registered route is a common mistake. A sitemap should list URLs you want indexed—not every endpoint Laravel knows about. Apply explicit inclusion rules at the model and route level.
Include these URL types
- Public marketing pages: home, about, contact, pricing, service landing pages.
- Indexable detail pages: blog posts, product pages, lawyer profiles, itinerary pages—each with a stable slug and 200 HTTP status.
- Paginated archives only when paginated URLs carry unique value; otherwise canonicalise to page one and omit pages 2+.
- Localised URLs when you ship hreflang alternates for Nepali and English content.
- Media sitemap entries for video landing pages if video is core to rankings; see video SEO optimisation for metadata alignment.
Exclude these URL types
- Authentication flows: login, register, password reset, email verification.
- Authenticated areas: dashboards, checkout steps behind session, client portals on apps like Mijar Law Associates.
- Internal search results, sort/filter query strings, and tracking parameters (
?utm_*, session IDs). - Duplicate paths: trailing-slash variants, uppercase slugs, print views, API routes.
- Noindex pages: thank-you screens, cart, paginated duplicates already consolidated by canonical tags.
- Soft-deleted or draft records—scope Eloquent with
->published()or->where('status', 'published').
Align sitemap URLs with your canonical tags and on-page SEO checklist. If a blog tag page is noindex, do not list it. If /services/web-design canonicalises to /services, list only the canonical target. Mismatch between sitemap and canonical is a recurring issue I've fixed on legal-tech portals where paginated practice-area archives leaked into XML.
Set accurate lastmod values
Google treats misleading lastmod dates as noise. Bind lastmod to real content changes—updated_at on meaningful edits, not automatic touch() on unrelated saves. For mostly static pages, omit lastmod or refresh only when copy changes. Use your JSON formatter to inspect API payloads when debugging CMS-driven lastmod sync.
How do you connect a Laravel sitemap to robots.txt and Search Console?
A generated sitemap nobody can find helps nothing. Wire discovery through robots.txt, internal links, and Google Search Console.
robots.txt declaration
Publish public/robots.txt or serve it dynamically. Point crawlers to your sitemap index:
User-agent: *
Disallow: /admin/
Disallow: /api/
Disallow: /login
Disallow: /cart
Sitemap: https://example.com/sitemap.xml Keep robots rules aligned with your broader crawl strategy in the robots.txt complete guide. Disallowing a path in robots does not remove it from index if external links exist; excluding it from the sitemap reduces crawl priority signals.
Search Console submission and monitoring
After deploy, open Google Search Console → Sitemaps → submit https://yourdomain.com/sitemap.xml. Monitor:
- Discovered URLs vs expected count from your database.
- Indexed lag on new sections—common after large migrations.
- Errors for 404s, redirects, or blocked URLs accidentally included.
For staging environments, block indexing entirely and skip GSC submission. A sitemap on a staging subdomain that leaks into production DNS has caused accidental indexation more than once in my deployment work.
How do you automate sitemap updates in a Laravel deployment pipeline?
On sister sites I maintain with Deployer 7 and GitLab CI, sitemap generation runs as a post-deploy task so the live symlink always serves fresh XML. Hook it after artisan migrate and cache clears:
task('artisan:sitemap', function () {
run('{{bin/php}} {{release_path}}/artisan sitemap:generate');
});
after('deploy:symlink', 'artisan:sitemap'); For high-churn content—news, deals on Ajako Deal, or booking inventory on Adventure Himalaya Nepal—also dispatch regeneration from model observers:
class PostObserver
{
public function saved(Post $post): void
{
if ($post->wasChanged(['slug', 'status', 'published_at'])) {
GenerateSitemapJob::dispatch()->delay(now()->addMinutes(2));
}
}
} Debounce queued jobs so ten rapid edits do not spawn ten full rebuilds on a 40,000-URL catalog. A two-minute delay coalesces bursts.
Performance and caching considerations
Generating a massive sitemap synchronously on every HTTP request will tank response times. Precompute to disk or object storage and serve statically. If you front the site with a CDN, as described in Cloudflare CDN setup, purge /sitemap.xml after regeneration or set a modest TTL (15–60 minutes). MySQL 9.7 and PostgreSQL 18 both handle the underlying queries fine when you select() only needed columns and chunk results—patterns that mirror database best practices in Laravel.
Multi-language and Nepali content
Law-firm and government-adjacent sites often ship Nepali and English URLs. Emit hreflang inside HTML; in sitemaps, list each language URL as its own entry pointing to the same content cluster, or use dedicated language sitemaps referenced from a sitemap index. When dates display in Bikram Sambat for users, keep sitemap lastmod in W3C datetime (ISO 8601) format—crawlers expect Gregorian timestamps. Content tools like the Nepali Unicode converter help editors publish UTF-8 clean copy that matches indexed URLs.
Testing before production
Validate output against Google's guidelines and the official Google Search Central sitemap documentation. Locally:
php artisan sitemap:generate
xmllint --noout public/sitemap.xml
grep -c '<loc>' public/sitemap.xml Cross-check a sample of <loc> values with curl -I for 200 status and self-referencing canonical headers. On apps using Meilisearch, ensure deindexed records disappear from the sitemap on the next run—search index and sitemap should share the same publish scope.
Key Takeaways
- Generate
sitemap.xmlfrom published Eloquent records and named routes withspatie/laravel-sitemap, not hand-maintained XML. - Include only self-canonical, indexable 200 URLs; exclude auth, filters, API paths, and noindex pages.
- Declare the sitemap in
robots.txt, submit once in Search Console, and monitor discovered vs indexed counts. - Run
sitemap:generateon a schedule and after deploy; debounce event-driven rebuilds on busy catalogs. - Split into a sitemap index when you approach 50,000 URLs or 50 MB uncompressed.
- Keep
lastmodhonest—bind it to real content changes, not automatic timestamp touches.
People Also Ask
Does Laravel have a built-in sitemap generator?
No. Laravel 12 and 13 ship routing, scheduling, and queues—but not XML sitemap generation. Use a package such as Spatie's or a custom Artisan command that writes to public/sitemap.xml. The Laravel task scheduling docs cover how to automate the command.
Where should sitemap.xml live in a Laravel project?
Place the generated file in public/sitemap.xml so it is available at https://yourdomain.com/sitemap.xml. Do not store it in storage/ unless you add a dedicated route to stream it—static public files are faster and easier for crawlers and CDNs.
How often should you regenerate a Laravel sitemap?
Nightly regeneration is enough for most brochure and legal-content sites. Daily or event-driven updates suit news, eCommerce, and marketplaces where products or listings change frequently. Always regenerate after deployments that add routes or migrations affecting slugs.
Can a sitemap fix pages that are not indexed?
A sitemap helps discovery; it does not guarantee indexation. Thin content, duplicate clusters, manual penalties, and server errors still block indexing. Fix canonicalisation, content quality, and crawl errors first—then ensure the URL appears in your Laravel-generated sitemap.
Ship a sitemap that matches your Laravel app—not a static afterthought
Laravel sitemap generator SEO best practices boil down to treating XML as compiled output from your real indexation rules: the same scopes that drive public menus, canonical tags, and published flags. Automate generation, keep robots.txt honest, validate after every major release, and split large catalogs before you hit protocol limits. If you want this wired correctly into a new build or an existing Laravel 12/13 codebase—alongside technical SEO and Laravel web development—contact us to review your URL architecture and deploy pipeline. For related reading, see modern Laravel architecture, zero-downtime migrations, and the Notary Nepal portfolio for a legal-tech site where crawl hygiene directly affects lead quality.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

