Skip to content

RSS/Atom Feeds Best Practices

DodaTech Updated 2026-06-22 8 min read

In this tutorial, you'll learn about RSS/Atom Feeds Best Practices. We cover key concepts, practical examples, and best practices.

RSS and Atom feeds allow users and aggregators to subscribe to your site's content updates, providing a reliable, platform-independent distribution channel that complements social media and email newsletters for reaching your audience.

What You'll Learn

Why It Matters

RSS feeds give users control over their content consumption without algorithmic filtering or platform dependency. For publishers, feeds drive consistent returning traffic, improve SEO through content syndication, and serve as a reliable backup when social media platforms change their algorithms or policies. Despite the rise of social media, RSS usage has grown among technical audiences -- developers, researchers, and power users prefer feeds for staying updated without noise. At DodaTech, our RSS feed delivers tutorial updates to subscribers within minutes of deployment, with zero dependency on social media platforms.

Real-World Use

A technical blog with 5,000 RSS subscribers sees 40% of its traffic come from feed readers like Feedly and NewsBlur. A podcast uses an RSS feed as its primary distribution channel to reach Apple Podcasts, Spotify, and Overcast simultaneously. A news site uses Atom feeds for machine-to-machine content syndication with partner sites and archive services.

Feed Architecture

flowchart LR
  A[Content Pages] --> B[Hugo Feed Template]
  B --> C[RSS 2.0 XML]
  B --> D[Atom XML]
  C --> E[Feed Readers Feedly, Inoreader]
  C --> F[Email Newsletters via RSS]
  C --> G[Social Auto-Posters]
  D --> H[API Consumers]
  D --> I[Archive Services]
  style B fill:#f90,color:#fff

RSS vs Atom Feed Comparison

Feature RSS 2.0 Atom Notes
Standard Older (2002) Newer (2005) Atom is IETF standard (RFC 4287)
Content type Description only Full content support Atom supports HTML content natively
Date format RFC 822 RFC 3339 Atom uses ISO 8601 dates
Unique IDs guid optional id required Atom enforces unique IDs
MIME type application/rss+xml application/atom+xml Different content negotiation
Extensions Namespace-based Built-in fields RSS uses atom:link for extensions
Parser support Universal Universal Both widely supported
Hugo built-in Yes (RSS 2.0) Yes (Atom) Hugo generates both by default

Hugo RSS Template Customization

Hugo includes a default RSS template at layouts/_default/rss.xml. Customize it to control what appears in the feed.

Custom RSS 2.0 Template

{{/* layouts/_default/rss.xml */}}
{{ $pages := .Site.RegularPages }}
{{ $limit := .Site.Config.Services.RSS.Limit | default 50 }}
{{ $pages = where $pages "Kind" "page" | first $limit -}}
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>{{ .Site.Title }}</title>
    <link>{{ .Site.BaseURL }}</link>
    <description>{{ .Site.Params.description }}</description>
    <language>{{ .Site.Language.Lang }}</language>
    <lastBuildDate>{{ now.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</lastBuildDate>
    <atom:link href="{{ "feed.xml" | absURL }}" rel="self" type="application/rss+xml"/>
    {{ range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</pubDate>
      <guid isPermaLink="true">{{ .Permalink }}</guid>
      <author>{{ with .Params.author }}{{ . }}{{ else }}{{ .Site.Params.author }}{{ end }}</author>
      <description>{{ .Description | html }}</description>
      {{ if .Truncated }}
      <content:encoded><![CDATA[{{ .Summary | html }}]]></content:encoded>
      {{ else }}
      <content:encoded><![CDATA[{{ .Content | html }}]]></content:encoded>
      {{ end }}
      {{ range (.GetTerms "tags") }}
      <category>{{ .LinkTitle }}</category>
      {{ end }}
    </item>
    {{ end }}
  </channel>
</rss>

Expected behavior: The feed displays the 50 most recent regular pages (configurable via services.rss.limit). Each item includes title, link, publication date, author, description (as plain text for feed reader preview), and full content wrapped in CDATA for readers that support it. Tags appear as categories.

Hugo Config for Feeds

# hugo.toml -- Feed configuration
title = "DodaTech Tutorials"
baseURL = "https://tutorials.dodatech.com"

[services]
  [services.rss]
    limit = 50

[outputFormats]
  [outputFormats.RSS]
    mediatype = "application/rss+xml"
    baseName = "feed"
    isPlainText = false
    rel = "alternate"
    noUgly = true

[outputs]
  home = ["HTML", "RSS"]
  section = ["HTML", "RSS"]

Expected behavior: Hugo generates /feed.xml at the site root and /static-sites/feed.xml for the section. The baseName = "feed" setting renames the output to feed.xml (instead of the default index.xml), which is more conventional for RSS feeds.

Atom Feed Template

Atom feeds are preferred for programmatic consumption and archive purposes.

Custom Atom Template

{{/* layouts/_default/atom.xml */}}
{{ $pages := where .Site.RegularPages "Kind" "page" | first 50 -}}
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>{{ .Site.Title }}</title>
  <subtitle>{{ .Site.Params.description }}</subtitle>
  <link href="{{ "atom.xml" | absURL }}" rel="self" type="application/atom+xml"/>
  <link href="{{ .Site.BaseURL }}" rel="alternate" type="text/html"/>
  <updated>{{ now.Format "2006-01-02T15:04:05Z07:00" }}</updated>
  <id>{{ "atom.xml" | absURL }}</id>
  <author>
    <name>{{ .Site.Params.author }}</name>
  </author>
  {{ range $pages }}
  <entry>
    <title>{{ .Title }}</title>
    <link href="{{ .Permalink }}" rel="alternate" type="text/html"/>
    <published>{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}</published>
    <updated>{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}</updated>
    <id>{{ .Permalink }}</id>
    <summary>{{ .Description }}</summary>
    <content type="html">
      <![CDATA[{{ .Content | html }}]]>
    </content>
    {{ range (.GetTerms "tags") }}
    <category term="{{ .LinkTitle }}"/>
    {{ end }}
  </entry>
  {{ end }}
</feed>

Expected behavior: The Atom feed follows RFC 4287 format. Each entry has a required id (page URL), published and updated timestamps, summary (description), and full HTML content. The feed validates against the Atom schema and works with all modern feed readers.

Feed Optimization and SEO

Add these to your base template so browsers and feed readers detect your feed automatically.

{{/* layouts/partials/head.html */}}
{{ with .Site.Home.OutputFormats.Get "RSS" -}}
  <link rel="alternate" type="application/rss+xml" href="{{ .RelPermalink }}" title="{{ $.Site.Title }}">
{{ end -}}
{{ with .Site.Home.OutputFormats.Get "ATOM" -}}
  <link rel="alternate" type="application/atom+xml" href="{{ .RelPermalink }}" title="{{ $.Site.Title }} - Atom">
{{ end -}}

Expected behavior: The HTML <head> includes <link rel="alternate"> tags pointing to both RSS and Atom feeds. Browsers show a feed icon in the address bar, and feed readers auto-discover the feed URL.

Full Content vs Summary Feeds

Approach Pros Cons Best For
Full content Readers read without clicking through Lower site traffic, higher bandwidth Podcasts, news alerts
Summary only Drives traffic to site, lower bandwidth Less convenient for readers Blogs, tutorials
Hybrid (first 500 chars) Balances convenience and traffic More complex to implement Most sites

Common Errors

1. Missing guid or Non-Unique IDs

RSS feeds without unique guid elements cause feed readers to show duplicate items every time they poll. Always set guid isPermaLink="true" to the page's permanent URL, which is inherently unique.

2. Incorrect Date Format

RSS requires RFC 822 dates (Mon, 02 Jan 2006 15:04:05 -0700). Atom requires RFC 3339 (2006-01-02T15:04:05Z07:00). Using the wrong format causes validation errors and feed reader rejection.

{{/* RSS correct */}}
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" }}</pubDate>

{{/* Atom correct */}}
<published>{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}</published>

Without the rel="self" link, feed readers cannot determine the feed's canonical URL. This causes issues when the feed is accessed from multiple URLs (e.g., https://site.com/feed.xml vs https://www.site.com/feed.xml).

4. Serving XML Without Proper Content-Type

Serving RSS as text/html instead of application/rss+xml confuses feed readers. Configure your CDN or static host to set the correct MIME type for .xml files.

# _headers file for Cloudflare Pages
/feed.xml
  Content-Type: application/rss+xml; charset=utf-8

/atom.xml
  Content-Type: application/atom+xml; charset=utf-8

5. Feed Compression Causing Issues

Some feed readers fail to decompress gzipped feeds. Serve feeds uncompressed for maximum compatibility, or test thoroughly with major readers (Feedly, Inoreader, NewsBlur) before enabling compression.

6. Ignoring Cache Headers for Feeds

Aggressively cached feeds may serve stale content. Set Cache-Control: max-age=3600 (1 hour) for feeds to balance freshness with CDN performance.

Practice Questions

1. What is the difference between RSS 2.0 and Atom feed formats?

RSS 2.0 is an older, less strict format with optional fields. Atom is a newer IETF standard (RFC 4287) with required unique IDs, full content support, and ISO 8601 date formatting. Atom is preferred for programmatic consumption.

2. Why is the guid element important in RSS feeds?

The guid (globally unique identifier) allows feed readers to track which items a user has already seen. Without unique guid values, readers may show every item as new every time the feed is polled.

3. How can you help feed readers discover your RSS feed automatically?

Add <link rel="alternate" type="application/rss+xml" href="/feed.xml"> to the HTML <head> section. Most browsers and feed readers detect this auto-discovery link.

4. What are the trade-offs between full content and summary RSS feeds?

Full content feeds are more convenient for readers but reduce site traffic because users read everything in their feed reader. Summary feeds drive more site visits but are less convenient. A hybrid approach (first paragraph or 500 characters) balances both.

5. Challenge: Create a section-specific RSS feed that only includes tutorials from the static-sites category.

Use Hugo's section RSS output. Each section automatically generates its own feed.xml (e.g., /static-sites/feed.xml). You can also create a custom template that filters by section and limits to specific content types.

Mini Project: Multi-Format Feed Setup for a Hugo Site

Set up comprehensive feed support for your Hugo site:

  1. Customize the RSS template to include full content with CDATA wrapping
  2. Add an Atom feed template as a secondary format
  3. Configure hugo.toml to generate feeds for the home page and each section
  4. Add auto-discovery link tags to the base template
  5. Create a /feeds/ page that lists all available feeds with links and descriptions
  6. Validate both feeds using the W3C Feed Validation Service
<!-- content/feeds.md -->
---
title: "Available Feeds"
---

Subscribe to DodaTech Tutorials using your preferred feed reader:

## Main Feeds

- [Full Site RSS Feed](/feed.xml) -- All tutorials across all categories
- [Full Site Atom Feed](/atom.xml) -- Atom format, same content

## Category Feeds

- [Static Sites](/static-sites/feed.xml) -- Static site and JAMstack tutorials
- [Game Development](/game-development/feed.xml) -- Game development tutorials

## Feed Readers

We recommend these feed readers:
- **Feedly** (Web, iOS, Android)
- **Inoreader** (Web, iOS, Android)
- **NewsBlur** (Web, iOS, self-hosted option)
- **Miniflux** (Self-hosted, minimalist)

Test the setup by:

  1. Running hugo server -D and visiting /feed.xml
  2. Validating with https://validator.w3.org/feed/
  3. Subscribing in Feedly or Inoreader
  4. Making a content change and confirming the feed updates within minutes
  5. Checking that the Content-Type header is application/rss+xml

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro