Generating RSS Feeds and Sitemaps in Laravel
RSS is not dead. In the developer community, RSS remains the gold standard for consuming content. If your blog doesn't have an RSS feed (/feed), many developers (including myself) won't subscribe.
Similarly, a Sitemap (/sitemap.xml) is the map you hand to Google's crawlers.
Here is how to implement both in a clean, minimal Laravel way.
1. The RSS Feed
We want to generate an XML response that adheres to the RSS 2.0 or Atom specification.
Route
// routes/web.php
Route::get('/feed', [FeedController::class, 'index'])->name('feed');
Controller
namespace App\Http\Controllers;
use App\Facades\Markdown;
use Illuminate\Http\Response;
class FeedController extends Controller
{
public function index()
{
$posts = Markdown::getAllPosts()
->reject(fn($post) => $post->draft)
->take(20);
$content = view('feeds.rss', [
'posts' => $posts,
'meta' => [
'title' => 'My Technical Blog',
'description' => 'Coding, Laravel, and Thoughts.',
'link' => url('/'),
'lang' => 'en-us',
]
])->render();
return response($content, 200, [
'Content-Type' => 'application/xml; charset=UTF-8',
]);
}
}
The View (resources/views/feeds/rss.blade.php)
<?= '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ $meta['title'] }}</title>
<link>{{ $meta['link'] }}</link>
<description>{{ $meta['description'] }}</description>
<language>{{ $meta['lang'] }}</language>
<atom:link href="{{ url('/feed') }}" rel="self" type="application/rss+xml" />
@foreach($posts as $post)
<item>
<title><![CDATA[{{ $post->title }}]]></title>
<link>{{ route('blog.show', $post->slug) }}</link>
<guid isPermaLink="true">{{ route('blog.show', $post->slug) }}</guid>
<description><![CDATA[{{ $post->description }}]]></description>
<pubDate>{{ $post->date->toRssString() }}</pubDate>
<author>me@example.com (Your Name)</author>
</item>
@endforeach
</channel>
</rss>
Pro Tip: Laravel's Carbon instance on $post->date has a handy toRssString() method specifically for this.
2. The Sitemap
Sitemaps are simpler. They strictly list URLs and modification dates.
Controller Method
public function sitemap()
{
$posts = Markdown::getAllPosts()->reject(fn($p) => $p->draft);
return response()->view('feeds.sitemap', [
'posts' => $posts
])->header('Content-Type', 'text/xml');
}
The View (resources/views/feeds/sitemap.blade.php)
<?= '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{{-- Static Pages --}}
<url>
<loc>{{ url('/') }}</loc>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>{{ url('/about') }}</loc>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
{{-- Blog Posts --}}
@foreach($posts as $post)
<url>
<loc>{{ route('blog.show', $post->slug) }}</loc>
<lastmod>{{ $post->date->toAtomString() }}</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
@endforeach
</urlset>
Packages vs Custom
There is a great package spatie/laravel-feed that handles this configurations.
Why go custom?
- Dependencies: For a simple personal blog, pulling in a package just to loop over an array and print XML might be overkill.
- Control: You have bite-sized control over the exact output format.
- Learning: It's good to understand the underlying XML structure.
However, if you have multiple feed streams or complex requirements, Spatie's package is the way to go.
Final Polish
Add a link tag to your main layout <head> so browsers and RSS readers can auto-discover the feed:
<link rel="alternate" type="application/rss+xml" title="My Tech Blog RSS Feed" href="{{ route('feed') }}" />
Now your content is open to the world, consumable by bots and humans alike.