---
title: "Add an RSS Feed AI Agents Can Actually Find"
slug: add-rss-feed-declare-in-head
published: 2026-09-24T08:32:34.065297+00:00
updated: 2026-09-24T08:32:34.065297+00:00
author: "Asif Rahman"
author_url: https://masifrahman.com
category: "AI Readiness"
tags: RSS feed, Atom feed, rel=alternate, content discovery, AI readiness, Hugo, Docusaurus, check:E5
description: "Hugo and Docusaurus both build an RSS feed by default, but only one declares it. How to check yours, fix the tag, and verify it with AIScan's E5 check."
url: https://aiscan.site/blog/add-rss-feed-declare-in-head
---

Two static-site generators build you a feed with zero configuration. One declares it in `<head>` on every site we tested. The other only manages it on five of every eight. Same default-on feature, same job, and the entire gap between them is one missing `<link>` tag.

Hugo and Docusaurus both write RSS or Atom to disk the moment you build the site, with no plugin and no setting to turn on. Live-tested across 15 sites on each platform in early September 2026: 8 of 15 Hugo sites serve a real feed at `/index.xml`, but 2 of those 8 never mention it anywhere in `<head>`, and a third points its `rel="alternate"` tag at the homepage instead of the feed. Docusaurus, measured the same way, hit 9 of 15 sites publishing `/blog/rss.xml` and `/blog/atom.xml`, and all nine declared both, automatically, with no configuration. The feed existing was never the finish line. The tag that tells a reader, human or machine, where to find it is.

## Quick summary

| Question | Short answer |
|---|---|
| What does a feed need besides existing? | A `<link rel="alternate" type="application/rss+xml">` (or `atom+xml`) tag in `<head>`, pointing at the feed's real URL |
| Fastest check | `curl -s https://yoursite.com \| grep -o 'rel="alternate"[^>]*'` |
| Which check this is | AIScan's E5, in the content dimension |
| Auto-declared, no edits needed | Docusaurus (9 of 9 tested), Squarespace, Ghost, WordPress |
| Builds the file but not the tag | Hugo (2 of 8 tested sites silent), Next.js, Astro |
| No native feed at all | Framer, Replit's default Express scaffold |
| Format | RSS 2.0 or Atom 1.0; either is fine, readers and agents parse both |
| Documented gap | Wix: no confirmed root feed path found first-party, verify on your own site before assuming one |
| What AIScan's E5 can't see yet | Whether the `<link>` target actually resolves to the feed, or quietly points at your homepage instead |

## Why a feed that exists can still be invisible

RSS and Atom autodiscovery works on one convention: a `<link>` element inside `<head>`, carrying `rel="alternate"` and a `type` of `application/rss+xml` or `application/atom+xml`, with an `href` pointing at the feed. A reader, an aggregator, or an agent doing periodic re-checks looks for that tag first. It does not walk a list of common paths hoping one resolves.

Think of it as an unlisted phone number: the line works, you can reach it if you already have the digits, but nobody finds it by looking you up. A blog with a perfectly valid feed at `/index.xml` and no `<link>` tag is in exactly that position, reachable by anyone who already knows the URL, invisible to anything that discovers sites the normal way. And the whole reason a feed is worth having is that it gives an outside system a fast, structured, dated list of what changed, instead of forcing it to re-crawl every page to find out. Skip the declaration and you built the shortcut without posting the sign.

This is check E5 in AIScan's content dimension, and it is a narrower check than it sounds: it looks for a working feed and a correct declaration, not for how good the feed's content is.

## Path one: your platform already builds the feed, so confirm the tag

If your platform ships a feed by default, the job is verification, not construction.

1. Find your feed's real URL from the table below.
2. Fetch your homepage's HTML and search for `rel="alternate"`: `curl -s https://yoursite.com | grep -o 'rel="alternate"[^>]*'`.
3. Confirm the `href` in that tag matches the URL from step 1: not your homepage, and not a stale path left over from a redesign.
4. If the tag is missing, wrong, or absent entirely, fix it where the table says to.

| Platform | Feed path | Declared automatically? | If it's missing |
|---|---|---|---|
| Docusaurus | `/blog/rss.xml` and `/blog/atom.xml` | Yes, 9 of 9 sites tested declared both | Nothing to fix; re-check after a theme swap |
| WordPress | `/feed/` | Yes, via `wp_head()` | A broken or custom theme can drop the `wp_head()` call; restore it |
| Ghost | `/rss/` | Yes, via the theme's `{{ghost_head}}` helper | Confirm `{{ghost_head}}` still renders in your theme's `<head>` partial |
| Squarespace | `<page-url>?format=rss` | Yes, injected on every page automatically | Nothing to fix |
| Hugo | `/index.xml` | No, it depends on the theme; 2 of 8 tested sites omitted it, and one pointed the tag at the homepage instead | Add a `<link rel="alternate" type="application/rss+xml" href="{{ \"index.xml\" \| absURL }}">` line to the theme's `<head>` partial |

Hugo is the sharp case here, because the gap isn't "no feed"; it's a feed that exists, works, and simply never gets mentioned. If your theme was copied or heavily customized, that one line is exactly the kind of thing that gets lost in the process, and nothing about the site looking finished will tell you it happened.

## Path two: your platform has no native feed, so you build both halves

Next.js, Astro, Webflow's custom code path, and Replit's default Express scaffold don't hand you a feed. You write the XML and the declaration yourself.

1. **Next.js (App Router):** create a route handler at `app/rss.xml/route.ts` that returns a `Response` with `content-type: application/rss+xml` and a hand-built XML string covering your post list. Then declare it once, site-wide, through the Metadata API rather than a hand-written tag: add `alternates: { types: { "application/rss+xml": "/rss.xml" } }` to your root `metadata` export, and Next.js renders the `<link>` tag for you on every page.
2. **Astro:** install `@astrojs/rss` and export a `GET` handler from `src/pages/rss.xml.js` that calls the package's `rss()` helper with your post collection. Astro writes a valid feed; it does not add the `<link>` tag, so add that by hand in your shared layout's `<head>`. Check the final URL against your `astro.config`: a global `trailingSlash: "always"` setting can serve the feed at `/rss.xml/` instead of `/rss.xml`, which breaks the exact URL you just declared.
3. **Webflow:** each CMS Collection has its own RSS toggle in the Collection's settings, publishing at `/<collection-slug>/rss.xml` once turned on (the exact label has moved between Webflow versions, so check your own Collection settings panel if this doesn't match what you see). Webflow does not add the discovery tag automatically; add it yourself through a Custom Code head embed in Site Settings.
4. **Replit:** if your app is a default Express or similar catch-all scaffold, add an explicit route (`app.get('/rss.xml', ...)`) that returns real XML with the right content type, and add the `<link>` tag in your HTML template's `<head>`. A catch-all with no matching route answers everything with your app shell and a 200, including a request for a feed that was never built, and that failure mode will not show up as an error anywhere in your logs.
5. **Framer:** there is no native feed of any kind on the platform as of this writing. The only path is external: publish your posts through a service that exposes its own feed, such as a headless CMS or a newsletter tool with a public archive, and link to that from your site. Otherwise your Framer site opts out of feed-based discovery entirely.

**Wix is a genuine gap, not an oversight.** We could not confirm a documented root feed path first-party: `/blog-feed.xml` and `/blog/feed.xml` both hard-404 on Wix's own marketing site, which doesn't run Wix Blog, and its homepage carries no `rel="alternate"` tag at all. If you run a Wix Blog site, check your own `<head>` for the tag before assuming any specific path; don't copy a URL from a guide that hasn't verified it against your account type.

## Verify it, don't assume it

Two checks, in order of effort:

1. **Fetch and grep.** `curl -s https://yoursite.com/your-feed-path` should return XML starting with `<?xml` and either `<rss version="2.0">` or `<feed xmlns="http://www.w3.org/2005/Atom">`. If you get HTML instead, your route isn't matching and something else, often a catch-all, is answering in its place.
2. **Confirm the declaration matches the file.** `curl -s https://yoursite.com | grep -o 'rel="alternate"[^>]*href="[^"]*"'` should print a `type` of `application/rss+xml` or `application/atom+xml` and an `href` that, fetched on its own, returns the same XML from step 1. A `<link>` tag pointing at your homepage, the exact failure the Hugo sweep found, passes a glance and fails this test immediately.

| Check | Pass looks like | Fail looks like |
|---|---|---|
| Fetch the feed URL directly | Starts with `<?xml`, then `<rss` or `<feed` | Starts with `<!DOCTYPE html>` |
| Fetch the homepage and read the tag's `href` | Resolves to the exact same XML as above | 404s, redirects elsewhere, or matches the homepage's own bytes |

For a third-party read, run `npx aiscan-cli yoursite.com` or paste the URL at [aiscan.site](https://aiscan.site/) and read check E5. It confirms the feed resolves and is declared; it does not yet independently verify that the declared `href` and the working feed are the same URL, so the manual step above is still worth the ten seconds even after a clean score.

## What this doesn't cover

A feed is not a substitute for `llms.txt`, a sitemap, or a correct 404; it's one more discovery surface, and check E5 grades only this one. It also won't help a site that publishes nothing on a schedule: a single static landing page with no blog or changelog has nothing worth putting in a feed, and adding an empty one just to pass a check is wasted effort. And a feed can silently break after you change themes or redesign your site, since the `<link>` tag lives in a template file that a new design doesn't automatically carry forward, so re-run the grep above after any theme change, not just once at launch.

On WordPress, this specific check needs nothing extra: `wp_head()` handles it without a plugin. If you're also cleaning up robots.txt, robots meta, schema, or llms.txt on the same WordPress site, [ThinkRank](https://thinkrank.ai) manages all of those from one plugin instead of three fighting over the same files, which is the more common source of AI-readiness regressions than the feed tag ever is.

## Next step

Run `npx aiscan-cli yoursite.com` (or paste your URL at [aiscan.site](https://aiscan.site/)) and read check E5 alongside D1 and C2. A site that declares its feed correctly alongside a correct `robots.txt` and `llms.txt` gives an agent three ways to find what changed instead of one. For the full platform-by-platform setup rather than just the feed, the [Hugo](https://aiscan.site/blog/ai-readiness-setup-hugo) and [Docusaurus](https://aiscan.site/blog/ai-readiness-setup-docusaurus) guides cover this same measurement in context, and every platform walkthrough on the blog is collected at [/guides](https://aiscan.site/guides).
