A Better RSS for Hugo
Tags: blog, staticsite
Published: 2020-08-21 11:26:38

Hugo. Best static blogging platform. See my other post about [migrating to Hugo](https://www.yuriburger.net/2020/06/23/migrating-to-hugo/).


#### Missing content
Hugo ships with a default RSS 2.0 template for basic feed functionality. For most feed readers this is probably ok, but for Syndication not so much. The thing is, that the feed XML only contains the basic fields, like link, title, date, description, etc. But what is missing, is the posts content. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gj7wx9ebu25gnqoyhzfu.png)

#### A better template
To create a better RSS template, we start with creating a rss.xml file in layouts/_default. You could use other locations and filenames, please see the Hugo documentation for more info on this. The default template is a good starting point, so make sure you copy that content into the rss.xml file first. You can find the default file here: https://gohugo.io/templates/rss/#the-embedded-rssxml

Now we can add our article content to the RSS items by adding a "content" tag (also notice the change to the "description" tag to allow for formatted summaries):
```
.....
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>

      <description>{{ "<![CDATA[" | safeHTML }} {{ .Summary }}]]></description>
      <content:encoded>{{ "<![CDATA[" | safeHTML }} {{ .Content }}]]></content:encoded>

    </item>
.....
```

To enable this change, the header needs to change too:

```
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" 
xmlns:content="http://purl.org/rss/1.0/modules/content/" 
xmlns:atom="http://www.w3.org/2005/Atom">
```

#### W3C Validation
After these changes, you need to make sure you end up with a valid RSS feed, so let's check this with the W3C Validation Service: https://validator.w3.org/feed/:
Open your your blog RSS feed (usually located at /blog/index.xml) and paste the raw xml content in the Validation Field, press "Validate" and see if it al works out. I ended up with valid RSS but with an important recommendation:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4zgrkida15m67ixq92ah.png)

This is of course bad for syndication, because we need an absolute URL to render the images directly from the source location.

#### Absolute links
So how to fix the "content:encoded should not contain relative URL references"?

I found some code on Hugo's Discourse that worked almost instantly. Thank you "hacdias" (https://discourse.gohugo.io/u/hacdias)!
Create a partial rss.html (in "layouts/partials") with the following content:

```
{{ $html := .Content | safeHTML }}

{{ $hrefs := findRE "href=\"([^\"]*)\"" $html }}
{{ range $href := $hrefs}}
  {{ $absHref := strings.TrimPrefix "href=\"" $href  }}
  {{ $absHref = strings.TrimSuffix "\"" $absHref  }}
  {{ $absHref = printf "href=\"%s\"" ($absHref | absURL) }}
  {{ $html = replace $html $href $absHref }}
{{ end }}

{{ $srcs := findRE "src=\"([^\"]*)\"" $html }}
{{ range $src := $srcs}}
  {{ $absSrc := strings.TrimPrefix "src=\"" $src  }}
  {{ $absSrc = strings.TrimSuffix "\"" $absSrc  }}
  {{ $absSrc = printf "src=\"%s\"" ($absSrc | absURL) }}
  {{ $html = replace $html $src $absSrc }}
{{ end }}

{{ $srcset := findRE "srcset=\"([^\"]*)\"" $html }}
{{ range $set := $srcset}}
  {{ $parts := strings.TrimPrefix "srcset=\"" $set  }}
  {{ $parts = strings.TrimSuffix "\"" $parts  }}
  {{ $parts = split $parts "," }}
  {{ $newSrcset := slice }}
  {{ range $part := $parts }}
    {{ $part = $part | replaceRE "^\\s*(.*)\\s*$" "$1" }}
    {{ $lg := split $part " " }}
    {{ $href := index $lg 0 | absURL }}
    {{ $size := index $lg 1 }}
    {{ $newSrcset = $newSrcset | append (printf "%s %s" $href $size) }}
  {{ end }}
  {{ $newSrcset = delimit $newSrcset ", " }}
  {{ $newSrcset = printf "srcset=\"%s\"" $newSrcset }}
  {{ $html = replace $html $set $newSrcset }}
{{ end }}

{{ return $html }}
```

And make sure we call the partial when rendering our feed XML:

```
<content:encoded>{{ "<![CDATA[" | safeHTML }} {{ partial "rss.html" . | safeHTML }}]]></content:encoded>
```

Another option is providing a "xml:base" URI and set it to the base URL of the site. Although not officially supported by W3C RSS 2.0, many readers and aggregators (reportedly) support it.

```
<?xml version="1.0"?>
  <rss version="2.0" xml:base="http://example.com/sub">
    <channel>
    .....
``` 

/Y.