An XML sitemap is one of the simplest ways to help search engines discover important URLs on a website.
For a small website, manually maintaining a sitemap may be enough. But as a Laravel application grows and starts generating hundreds or thousands of dynamic URLs, maintaining a static sitemap becomes difficult.
In this tutorial, we'll build a practical XML sitemap system in Laravel that generates URLs dynamically from database records.
We'll cover:
- What an XML sitemap is
- Why Laravel applications benefit from dynamic sitemaps
- Creating a sitemap controller
- Generating XML responses
- Adding database URLs
- Creating a sitemap index
- Handling multiple content types
- Adding a sitemap to
robots.txt - Using canonical URLs
- Handling large datasets
- Caching sitemap data
- Testing the final sitemap
What Is an XML Sitemap?
An XML sitemap is a file containing URLs that you want search engines to discover.
A basic sitemap looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<url>
<loc>https://example.com/</loc>
</url>
<url>
<loc>https://example.com/about</loc>
</url>
<url>
<loc>https://example.com/blog/my-first-post</loc>
</url>
The <loc> element contains the URL.
A sitemap does not guarantee that a page will be indexed or ranked. Its main purpose is to help search engines discover URLs efficiently.
Why Use a Dynamic Sitemap in Laravel?
Laravel applications frequently generate pages from database records.
For example, an application may contain:
- Blog posts
- Products
- Authors
- Categories
- Books
- Courses
- Documentation
- Tools
Instead of manually adding every URL to a sitemap, Laravel can generate the URLs from your application's database.
This means that when new public content is published, it can automatically become part of the sitemap.
Likewise, unpublished or deleted content can be removed from the sitemap.
Step 1: Create a Sitemap Controller
Create a controller using Artisan:
php artisan make:controller SitemapController
Then create a basic sitemap method:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Response;
class SitemapController extends Controller
{
public function index(): Response
{
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
$xml .= '<url>';
$xml .= '<loc>' . url('/') . '</loc>';
$xml .= '</url>';
$xml .= '</urlset>';
return response($xml)
->header('Content-Type', 'application/xml');
}
}
This creates a simple XML response containing your homepage.
Step 2: Add the Sitemap Route
Open:
routes/web.php
Add:
use App\Http\Controllers\SitemapController;
Route::get('/sitemap.xml', [SitemapController::class, 'index'])
->name('sitemap');
Now open:
https://example.com/sitemap.xml
You should see the XML sitemap.
Step 3: Generate URLs From the Database
The real benefit of a Laravel sitemap comes from generating URLs dynamically.
Imagine that your application has a Post model.
Import the model:
use App\Models\Post;
Then retrieve published posts:
$posts = Post::where('status', 'published')
->latest('updated_at')
->get();
Now add each post to the sitemap:
foreach ($posts as $post) {
$xml .= '';
$xml .= '' . e(route('posts.show', $post->slug)) . '';
$xml .= '' . $post->updated_at->toAtomString() . '';
$xml .= '';
}
The complete controller could look like this:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Response;
class SitemapController extends Controller
{
public function index(): Response
{
$posts = Post::where('status', 'published')
->latest('updated_at')
->get();
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
foreach ($posts as $post) {
$xml .= '<url>';
$xml .= '<loc>' . e(route('posts.show', $post->slug)) . '</loc>';
$xml .= '<lastmod>' . $post->updated_at->toAtomString() . '</lastmod>';
$xml .= '</url>';
}
$xml .= '</urlset>';
return response($xml)
->header('Content-Type', 'application/xml');
}
}
Step 4: Add Multiple Content Types
Most real Laravel applications have several types of public content.
For example:
/posts/...
/products/...
/authors/...
/categories/...
You can retrieve each type separately:
$posts = Post::where('status', 'published')->get();
$products = Product::where('status', 'active')->get();
$authors = User::where('is_author', true)->get();
You can then generate sitemap URLs for each content type.
This approach is useful for content-driven platforms and marketplaces.
For example, a platform such as WebDecky contains multiple types of public resources, including SEO tools, web utilities, digital products, blog content, and community features.
Step 5: Use a Sitemap Index for Larger Websites
As a website grows, separating sitemap files by content type can make the system easier to maintain.
For example:
sitemap-index.xml
sitemap-pages.xml
sitemap-posts.xml
sitemap-products.xml
sitemap-authors.xml
sitemap-categories.xml
A sitemap index can look like this:
<?xml version="1.0" encoding="UTF-8"?>
<sitemap>
<loc>https://example.com/sitemap-pages.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap-posts.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap-products.xml</loc>
</sitemap>
<sitemap>
<loc>https://example.com/sitemap-authors.xml</loc>
</sitemap>
This structure allows different content types to be generated independently.
Step 6: Create the Sitemap Index
You can create a dedicated controller method:
public function index(): Response
{
$sitemaps = [
url('/sitemap-pages.xml'),
url('/sitemap-posts.xml'),
url('/sitemap-products.xml'),
url('/sitemap-authors.xml'),
];
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml .= '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
foreach ($sitemaps as $sitemap) {
$xml .= '<sitemap>';
$xml .= '<loc>' . e($sitemap) . '</loc>';
$xml .= '</sitemap>';
}
$xml .= '</sitemapindex>';
return response($xml)
->header('Content-Type', 'application/xml');
}
Then add the route:
Route::get('/sitemap-index.xml', [
SitemapController::class,
'index'
]);
Step 7: Add the Sitemap to robots.txt
Your robots.txt can point search engines to the sitemap index:
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap-index.xml
For example, WebDecky provides its sitemap index at:
https://webdecky.com/sitemap-index.xml
This gives crawlers another way to discover the site's public URLs.
Step 8: Only Include Public URLs
A sitemap should contain URLs that you actually want search engines to discover.
Avoid adding private or administrative URLs such as:
/admin
/login
/register
/cart
/checkout
/user/settings
/private-content
Instead, focus on useful public pages.
For a digital marketplace, examples might include:
/products/laravel-admin-panel
/products/seo-tool
/authors/example-author
/categories/laravel
For a blog:
/blog/laravel-seo-guide
/blog/xml-sitemap-guide
/blog/php-performance-guide
The goal is to help search engines discover your important public content.
Step 9: Use Canonical URLs
The URLs in your sitemap should normally represent the preferred canonical versions of your pages.
For example, suppose these URLs show the same page:
https://example.com/product/tool
https://example.com/product/tool?ref=facebook
The sitemap should generally contain the preferred URL:
https://example.com/product/tool
rather than tracking variations.
Canonicalization is important when multiple URLs can represent the same content.
Step 10: Handle Large Datasets
If your application contains thousands of records, avoid loading everything into memory unnecessarily.
Instead of:
$posts = Post::all();
you can process records in chunks:
Post::where('status', 'published')
->chunkById(1000, function ($posts) use (&$xml) {
foreach ($posts as $post) {
$xml .= '<url>';
$xml .= '<loc>' . e(route('posts.show', $post->slug)) . '</loc>';
$xml .= '</url>';
}
});
Chunking can reduce memory usage when processing large datasets.
For very large applications, generating multiple sitemap files is usually a cleaner architecture than producing one enormous XML response.
Step 11: Cache Generated Sitemap Data
If your sitemap contains thousands of URLs, generating it from the database on every request may create unnecessary database queries.
Laravel's cache system can help.
For example:
$xml = Cache::remember(
'sitemap-posts',
now()->addHours(6),
function () {
// Generate sitemap XML here.
}
);
You can refresh or clear the cache when important content changes.
This can reduce repeated database work.
Step 12: Test Your Sitemap
After implementing the system, test your sitemap URLs.
For example:
You should also test individual sitemap files if your application provides them.
Check:
- XML is valid
- URLs are publicly accessible
- URLs return appropriate HTTP responses
- URLs are canonical
- Unpublished content is excluded
- Private content is excluded
- Duplicate URLs are avoided
- Sitemap files are not unnecessarily large
A Practical Laravel Sitemap Architecture
For a growing Laravel application, I prefer an architecture like:
sitemap-index.xml
│
├── sitemap-pages.xml
├── sitemap-posts.xml
├── sitemap-products.xml
├── sitemap-authors.xml
├── sitemap-categories.xml
└── sitemap-other-content.xml
The sitemap index becomes the central entry point.
Each sitemap can then be responsible for one particular type of content.
This makes the system easier to maintain as the application grows.
Example Route Structure
Your routes could look like this:
Route::get('/sitemap-index.xml', [
SitemapController::class,
'index'
]);
Route::get('/sitemap-pages.xml', [
SitemapController::class,
'pages'
]);
Route::get('/sitemap-posts.xml', [
SitemapController::class,
'posts'
]);
Route::get('/sitemap-products.xml', [
SitemapController::class,
'products'
]);
Route::get('/sitemap-authors.xml', [
SitemapController::class,
'authors'
]);
This keeps sitemap generation organized and makes it easier to add additional content types later.
Connecting Your Sitemap to Google Search Console
After your sitemap is working, you can submit the sitemap index through Google Search Console.
For example:
sitemap-index.xml
You can also include the sitemap location in robots.txt.
Remember that submitting a sitemap does not guarantee that every URL will be indexed or ranked.
It simply gives search engines another way to discover your site's URLs.
A Real-World Laravel Example
I use this type of sitemap architecture for WebDecky, a Laravel-based platform combining SEO tools, web utilities, a digital marketplace, blog content, and community features.
Because different parts of the platform generate different types of URLs, separating sitemap generation by content type makes the SEO infrastructure easier to maintain.
For example, the public website contains different types of pages that can be discovered through the site's sitemap system.
You can explore the live platform here:
And you can inspect the sitemap index here:
The important lesson is that you don't need to manually edit a sitemap every time new content is published.
Laravel can generate sitemap URLs directly from your application's data.
SEO Benefits of a Well-Designed Sitemap
A properly implemented sitemap can help with:
- Discovering new URLs
- Discovering updated content
- Organizing large websites
- Helping crawlers find important public pages
- Managing different content types
- Making technical SEO easier to maintain
However, a sitemap is only one part of technical SEO.
You should also pay attention to:
- Page quality
- Internal linking
- Canonical URLs
- HTTP status codes
- Mobile usability
- Page performance
- Structured data where appropriate
- Crawlable navigation
- Useful, original content
Final Thoughts
A dynamic XML sitemap is a relatively small Laravel feature that can make a large application much easier to manage.
The basic workflow is:
Database
↓
Laravel Models
↓
Sitemap Controller
↓
XML Sitemap
↓
Sitemap Index
↓
Search Engines
For a small website, one sitemap may be enough.
For a larger Laravel application, separating sitemaps by content type provides a cleaner and more scalable architecture.
Most importantly, remember that a sitemap is about discoverability, not guaranteed rankings.
Build useful content, maintain good internal linking, use canonical URLs, keep your public pages accessible, and use your sitemap as one part of a broader technical SEO strategy.
If you're building a Laravel application with many dynamic pages, implementing the sitemap system early can save you a lot of maintenance later.
Top comments (0)