---
title: "How to publish an RSS/Atom/JSON feed and declare it in the head on Docusaurus"
slug: rss-feed-docusaurus
published: 2026-09-29T13:15:42.227254+00:00
updated: 2026-09-29T13:15:42.227254+00:00
author: "Asif Rahman"
author_url: https://masifrahman.com
category: "AI Readiness"
tags: check:E5, platform:docusaurus, Docusaurus, RSS, Atom, JSON Feed, feedOptions, blog plugin
description: "Docusaurus writes RSS and Atom by default, but four setups lose the feed. Find yours, restore it, add JSON Feed and verify with AIScan check E5."
url: https://aiscan.site/blog/rss-feed-docusaurus
---

Docusaurus is the one platform in this series where the feed usually exists before you ask for it. `@docusaurus/preset-classic` includes the blog plugin, and the blog plugin writes RSS and Atom files on every production build. So the fix is rarely "add a feed". It is "find out why yours is missing", and there are four causes, each with a one-line repair.

## Quick summary

| | |
|---|---|
| **Check** | E5 (Content feed: RSS, Atom or JSON Feed) |
| **Platform** | Docusaurus 3.x |
| **Default** | RSS and Atom at `/blog/rss.xml` and `/blog/atom.xml`, declared in `<head>` automatically |
| **Four ways it goes missing** | Blog plugin disabled, `feedOptions.type` set to `null`, checking a dev server, or a wrong `url` in the config |
| **Optional upgrade** | `type: 'all'` adds JSON Feed at `/blog/feed.json` |
| **Time** | About 5 minutes |
| **Verify** | `npx aiscan-cli yoursite.com`, then `curl` the feed and grep the `<head>` |

## Why the feed exists, and why it can vanish

The blog plugin does not read your Markdown a second time to build the feed. According to the [plugin's own reference page](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog), the feed "works by extracting the build output, and is only active in production". That one sentence explains most support threads about missing feeds: the plugin needs a finished build to read from.

AIScan's survey of 15 live Docusaurus sites, described in [the complete Docusaurus setup guide](https://aiscan.site/blog/ai-readiness-setup-docusaurus), found 9 serving `/blog/rss.xml`, and all 9 declared it in `<head>`. The other 6 are the sites this guide is for. A site with no blog folder has no feed to publish, and a site that switched the plugin off has none either.

## Step 1: Decide which of the four causes you have

| Symptom | Cause | Go to |
|---|---|---|
| `/blog/rss.xml` returns 404 on the live site and you have no `blog/` folder | You run docs only, or the blog plugin is off | Step 2 |
| The blog exists but the feed 404s | `feedOptions.type` is `null` | Step 3 |
| The feed works on the live site but 404s on `localhost:3000` | You are on `docusaurus start`, which never builds feeds | Step 4 |
| The feed loads but every link inside points at the wrong host | `url` in `docusaurus.config.js` is wrong | Step 5 |

**How to know which one:** run `curl -sI https://yoursite.com/blog/rss.xml`. A 200 with `application/xml` means the feed exists and you only need Step 5 or the head check. A 404 means Steps 2 to 4.

## Step 2: Turn the blog plugin on

Docusaurus supports a docs-only mode, documented under **Docs > Docs-only mode**, where the preset is configured with `blog: false`. That is a legitimate setup, but it produces no feed at all.

If you want a feed, restore the blog options in `docusaurus.config.js`:

```js
presets: [
  [
    '@docusaurus/preset-classic',
    {
      blog: {
        showReadingTime: true,
        feedOptions: { type: ['rss', 'atom'] },
      },
    },
  ],
],
```

Then add at least one post under `blog/`. A blog with zero posts has nothing to syndicate.

If you would rather keep the site docs-only, a feed is not required. Say so in your own notes and accept the E5 warning; a feed with no dated content is worse than no feed.

## Step 3: Turn feed generation back on

The reference page states the defaults directly: `feedOptions` defaults to `{type: ['rss', 'atom']}`, and "Use `null` to disable generation". Search your config for `type: null` and delete it, or set it explicitly:

```js
feedOptions: {
  type: 'all',
  title: 'Your Site Blog',
  description: 'New posts, changelogs and release notes',
  copyright: `Copyright © ${new Date().getFullYear()} Your Company`,
  limit: 50,
},
```

Three things in that block matter:

- **`type: 'all'`** adds JSON Feed at `/blog/feed.json` next to RSS and Atom. The blog guide lists all three URLs under **Docs > Blog > Feed**. [JSON Feed 1.1](https://www.jsonfeed.org/version/1.1/) and [RFC 4287 (Atom)](https://www.rfc-editor.org/rfc/rfc4287.html) are the specs the check validates against.
- **`limit`** defaults to 20 posts. Set it higher, or to `null` for every post, if agents should see your full history.
- **Author emails.** The blog guide states that "RSS feeds require the author's email to be set for the author to appear in the feed". Add `email` to each entry in `blog/authors.yml` if you want bylines in RSS.

## Step 4: Test a production build, not the dev server

`npm run start` will never show a feed. Build first, then serve the output:

```bash
npm run build
npm run serve
curl -sI http://localhost:3000/blog/rss.xml
```

The CLI reference lists `docusaurus serve` as serving your built site locally (default port 3000, output directory `build`). Expect `HTTP/1.1 200 OK` and `application/xml`.

## Step 5: Check `url` and `baseUrl`

Feed items use absolute links built from the `url` field in `docusaurus.config.js`. If `url` still says `https://your-docusaurus-site.example.com` or `http://localhost`, every `<link>` in the feed points nowhere. Set it to your production origin, with no trailing slash and no path:

```js
url: 'https://yoursite.com',
baseUrl: '/',
```

Sites deployed under a subpath set `baseUrl` to something like `'/repo-name/'`, so request the feed at that prefix and confirm it after each deploy.

## Multiple blogs get multiple feeds

A site running several blog plugin instances gets one feed per instance. docusaurus.io itself does this: it serves `/blog/rss.xml` and a separate `/changelog/rss.xml`, and its `<head>` declares both, plus a third blog under `/tests/blog/`. If you run a second instance for release notes, check that each feed is declared and returns 200.

## Verify it worked

Lead with the scan. Run `npx aiscan-cli yoursite.com` and look at check E5, or paste the URL at [aiscan.site](https://aiscan.site/). By hand, the equivalent is:

```bash
curl -s https://yoursite.com/blog/rss.xml | head -c 300
curl -s https://yoursite.com/ | grep -o '<link[^>]*alternate[^>]*>'
```

**Expected output:** the first command starts with `<?xml version="1.0"` and an `<rss version="2.0"` element with your title inside `<channel>`. The second prints at least one `<link rel="alternate" type="application/rss+xml" href="/blog/rss.xml" ...>` tag, plus an Atom tag with `application/atom+xml`. On docusaurus.io the tags appear on the homepage and on individual blog posts alike, so any page can be your test target.

**If it fails:** a 404 on the live site after a green build means the deploy served an old `build/` directory. Clear the cache with `npm run clear`, rebuild and redeploy.

## Maintenance

Every new post regenerates the feed on the next build, so there is nothing to update by hand. Recheck after upgrading Docusaurus, changing `routeBasePath`, or adding a second blog instance. The plugin currently ships in version 3.10.2 according to its reference page.

## Ship it, then check the neighbours

A feed is the cheapest changed-since-last-week signal a Docusaurus site can publish. Run the scan again, then look at the other rows in the [content dimension](https://aiscan.site/docs/checks/content). The same site probably also needs [an llms.txt](https://aiscan.site/blog/llms-txt-docusaurus) and [an AI-aware robots.txt](https://aiscan.site/blog/robots-txt-ai-bots-docusaurus), since Docusaurus generates neither. The same live-route-versus-generated-file question appears on other platforms, for example [Lovable](https://aiscan.site/blog/rss-feed-lovable). The full list of checks is at [/guides](https://aiscan.site/guides).

