Hugo is a great static site generator, but it can be a bit inscrutable in some of the decisions it makes. It’s default RSS template is fairly opinionated in what it decides to include/exclude, but it can be easily overridden to match what you want your RSS feed to contain.

All the below tips involve overriding Hugo’s default RSS template.

To get started:

  1. Grab a copy of that template, and paste it into your site at layouts/_default/rss.xml.
  2. We’ll be making updates to the line that originally reads as {{ range $pages }}. As of writing, it’s line 23.

Include all pages in the main RSS feed

By default, the root index.rss only contains content directly under the root directory. If your blog lives under a subsection (as it likely does), then by default your posts won’t show up in your site’s root RSS feed.

Supposed your site looks something like this:


Then, index.rss will only contain a reference to the generated about.html and blog/index.html – but not blog/my_post.html.

If you want all your pages to appear in the root RSS feed, you can change the template to loop over all .Site.Pages:

<!-- Change the original range expression to the following: -->
{{ range .Site.Pages }}

Include only pages for a certain section

For my site, the only content that I want to have included in my RSS is pages under the “blog” section. We can use a where filter to only include pages that are in a certain section:

<!-- Change the original range expression to the following: -->
{{ range (where .Site.Pages ".Section" "blog") }}

Note: Some themes use “posts” as the section name for blog posts, so adjust your template accordingly.

Exclude “section” indices from RSS feeds

Additionally, Hugo includes a bunch of non-post pages in your RSS. For example, if you have an “About” page, or a landing page for your blog section, these pages may get included in your RSS.

The only pages that I want to have show up in my RSS feed are actual blog posts. Fortunately we can easily exclude anything that isn’t a page (i.e. a blog post).

<!-- Change the original range expression to the following: -->
{{ range where $pages "Kind" "page" }}

Putting it all together

Hugo allows you to chain “where” filters, so you can combine all the above tips into a single expression:

{{ range where (where .Site.Pages ".Section" "blog") "Kind" "page" }}

This changes the RSS template so that it displays all posts under the “blog” section, but doesn’t include the base “blog/index.html” section page or any other taxonomy/section pages.

For completeness, this is what my final RSS template looks like:

<!-- /layouts/_default/rss.xml -->
<rss version="2.0"
        <title>{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
        <link>{{ .Permalink }}</link>
        <description>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
        <generator>Hugo --</generator>{{ with .Site.LanguageCode }}
        <language>{{.}}</language>{{end}}{{ with }}
        <managingEditor>{{.}}{{ with $ }} ({{.}}){{end}}</managingEditor>{{end}}{{ with }}
        <webMaster>{{.}}{{ with $ }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
        <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
        <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
    {{ with .OutputFormats.Get "RSS" }}
        {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
    {{ end }}
    {{ range where (where .Site.Pages ".Section" "blog") "Kind" "page" }}
<title>{{ .Title }}</title>
<link>{{ .Permalink }}</link>
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{ with }}<author>{{.}}{{ with $ }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
<description>{{- .Content | html -}}</description>
    {{ end }}