mykiwi/mykiwi.blogpublic Fork
⑂ d59cce3
Commits
⬇ Clone ▾
git clone https://git.rickub.com/mykiwi/mykiwi.blog.git
git clone ssh://git@rickub.com/mykiwi/mykiwi.blog.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Track feed readers with a campaign tag and a pixel

Feed readers send no referrer and run no JavaScript. Tag each item link with utm_campaign=rss so clicks land under GoatCounter's Campaigns, and end each item's description with a tracking pixel. The guid keeps the bare permalink.
Romain Gautier committed 2026-09-25T00:00:49+02:00 Browse files
d59cce3 parent: c6cef41
modified blog/layouts/_default/rss.xml +16 -4
@@ -1,14 +1,21 @@
11 {{- /*
22 A copy of Hugo's embedded rss.xml (tpl/tplimpl/embedded/templates/rss.xml,
33 v0.166.0) kept deliberately close to it, so a future Hugo's version can be
4- diffed against this one. Three things are ours:
4+ diffed against this one. Five things are ours:
55
66 1. the <?xml-stylesheet?> below, which asks a browser to render the feed
77 through feed.xsl instead of showing its tag tree;
88 2. the <script>, which does the same job for browsers that no longer run
99 XSLT - see the note in hugo.toml;
1010 3. the channel <description>, which said "Recent content on mykiwi.blog"
11- and now says what the site's own <meta name="description"> says.
11+ and now says what the site's own <meta name="description"> says;
12+ 4. each item's <link> carries ?utm_campaign=rss, which GoatCounter files
13+ under Campaigns - a feed reader sends no referrer, so those clicks
14+ would otherwise all be "(unknown)". The <guid> stays the bare
15+ permalink: changing it would mark every post as new in every reader;
16+ 5. a GoatCounter tracking pixel at the end of each item's description.
17+ Readers run no JavaScript, so count.js never sees them; an <img> is
18+ the one thing many of them do fetch. See the note where it is built.
1219
1320 Both files are home-page outputs, so a section feed (posts/index.xml) has to
1421 reach across to site.Home for them rather than ask its own .OutputFormats.
@@ -59,6 +66,11 @@
5966 {{- else }}
6067 {{- $pages = $pctx.Pages }}
6168 {{- end }}
69+{{- /* Same host-then-path shape as baseof.html's window.goatcounter.path, so
70+ the feed shows up in GoatCounter next to the pages of the language it is
71+ the feed for. Only readers that load remote images are counted, once per
72+ item they display, so this is a floor and not a subscriber count. */}}
73+{{- $pixel := printf "<img src=\"https://stats.mykiwi.blog/count?p=%s/feed\" alt=\"\" width=\"1\" height=\"1\">" (urls.Parse site.Home.Permalink).Host }}
6274 {{- $limit := .Site.Config.Services.RSS.Limit }}
6375 {{- if ge $limit 1 }}
6476 {{- $pages = $pages | first $limit }}
@@ -109,11 +121,11 @@
109121 {{- range $pages }}
110122 <item>
111123 <title>{{ .Title }}</title>
112- <link>{{ .Permalink }}</link>
124+ <link>{{ .Permalink }}?utm_campaign=rss</link>
113125 <pubDate>{{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
114126 {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
115127 <guid>{{ .Permalink }}</guid>
116- <description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>
128+ <description>{{ printf "%s%s" .Summary $pixel | transform.XMLEscape | safeHTML }}</description>
117129 </item>
118130 {{- end }}
119131 </channel>
@@ -1,14 +1,21 @@
1 {{- /*1 {{- /*
2 A copy of Hugo's embedded rss.xml (tpl/tplimpl/embedded/templates/rss.xml,2 A copy of Hugo's embedded rss.xml (tpl/tplimpl/embedded/templates/rss.xml,
3 v0.166.0) kept deliberately close to it, so a future Hugo's version can be3 v0.166.0) kept deliberately close to it, so a future Hugo's version can be
4- diffed against this one. Three things are ours:4+ diffed against this one. Five things are ours:
5 5
6 1. the <?xml-stylesheet?> below, which asks a browser to render the feed6 1. the <?xml-stylesheet?> below, which asks a browser to render the feed
7 through feed.xsl instead of showing its tag tree;7 through feed.xsl instead of showing its tag tree;
8 2. the <script>, which does the same job for browsers that no longer run8 2. the <script>, which does the same job for browsers that no longer run
9 XSLT - see the note in hugo.toml;9 XSLT - see the note in hugo.toml;
10 3. the channel <description>, which said "Recent content on mykiwi.blog"10 3. the channel <description>, which said "Recent content on mykiwi.blog"
11- and now says what the site's own <meta name="description"> says.11+ and now says what the site's own <meta name="description"> says;
12+ 4. each item's <link> carries ?utm_campaign=rss, which GoatCounter files
13+ under Campaigns - a feed reader sends no referrer, so those clicks
14+ would otherwise all be "(unknown)". The <guid> stays the bare
15+ permalink: changing it would mark every post as new in every reader;
16+ 5. a GoatCounter tracking pixel at the end of each item's description.
17+ Readers run no JavaScript, so count.js never sees them; an <img> is
18+ the one thing many of them do fetch. See the note where it is built.
12 19
13 Both files are home-page outputs, so a section feed (posts/index.xml) has to20 Both files are home-page outputs, so a section feed (posts/index.xml) has to
14 reach across to site.Home for them rather than ask its own .OutputFormats.21 reach across to site.Home for them rather than ask its own .OutputFormats.
@@ -59,6 +66,11 @@
59 {{- else }}66 {{- else }}
60 {{- $pages = $pctx.Pages }}67 {{- $pages = $pctx.Pages }}
61 {{- end }}68 {{- end }}
69+{{- /* Same host-then-path shape as baseof.html's window.goatcounter.path, so
70+ the feed shows up in GoatCounter next to the pages of the language it is
71+ the feed for. Only readers that load remote images are counted, once per
72+ item they display, so this is a floor and not a subscriber count. */}}
73+{{- $pixel := printf "<img src=\"https://stats.mykiwi.blog/count?p=%s/feed\" alt=\"\" width=\"1\" height=\"1\">" (urls.Parse site.Home.Permalink).Host }}
62 {{- $limit := .Site.Config.Services.RSS.Limit }}74 {{- $limit := .Site.Config.Services.RSS.Limit }}
63 {{- if ge $limit 1 }}75 {{- if ge $limit 1 }}
64 {{- $pages = $pages | first $limit }}76 {{- $pages = $pages | first $limit }}
@@ -109,11 +121,11 @@
109 {{- range $pages }}121 {{- range $pages }}
110 <item>122 <item>
111 <title>{{ .Title }}</title>123 <title>{{ .Title }}</title>
112- <link>{{ .Permalink }}</link>124+ <link>{{ .Permalink }}?utm_campaign=rss</link>
113 <pubDate>{{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>125 <pubDate>{{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
114 {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}126 {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }}
115 <guid>{{ .Permalink }}</guid>127 <guid>{{ .Permalink }}</guid>
116- <description>{{ .Summary | transform.XMLEscape | safeHTML }}</description>128+ <description>{{ printf "%s%s" .Summary $pixel | transform.XMLEscape | safeHTML }}</description>
117 </item>129 </item>
118 {{- end }}130 {{- end }}
119 </channel>131 </channel>