First CH Apps / RSS/Atom Feed Validator
Enter a feed URL or a site URL: we discover the feed, fetch it, and check its XML syntax, required elements, item list, character encoding and date formats.
A feed URL or a site URL both work. Given a site URL we discover the feed first, then check its XML syntax, required elements, item list, character encoding and date formats. RSS 2.0, Atom 1.0 and RSS 1.0 (RDF) are supported, and feeds using the itunes namespace also get the podcast required-element check.
We request only the URL you enter (plus the candidates found during feed discovery). The feed itself is never re-served — you get the findings and short, truncated titles, dates and links. sitemap.xml validation belongs to the sitemap.xml Validator.
e.g. https://example.com/feed/ (validated directly) or https://example.com/ (we look for the site's feed first)
https://example.com/): if the response is an HTML page, we read up to 5 feed declarations (<link rel="alternate" type="application/rss+xml"> or application/atom+xml) from it. If the page declares none, we try 6 common paths instead: /feed/, /feed, /rss.xml, /atom.xml, /index.xml and /feed.xml. Each candidate is checked by its first few KB, and the first one that starts like RSS or Atom is validated in full.https://example.com/feed/, or https://example.com/?feed=rss2 — query strings are kept): if the response starts with <rss>, <feed> or <rdf:RDF>, it is validated directly and no discovery runs.user:pass@…) are rejected. Do not enter tokens or other secrets either — the fetch is a plain public GET with no cookies or credentials.Three outcomes to tell apart
<channel> needs a title, link and description (a missing one is reported as missing_required with channel/title and so on). Each <item> should have a title, an absolute <link>, a <guid> and a <pubDate> in RFC 822 form — Mon, 12 Oct 2026 09:00:00 +0900.<feed> needs the Atom namespace plus a title, id and updated (feed/title, feed/id, feed/updated). Each <entry> should have a title, an absolute <link rel="alternate" href>, an <id> and an <updated> or <published> date in RFC 3339 form — 2026-10-12T09:00:00+09:00.rel="self" link, newest-first order, and whether the declared encoding matches the bytes.Example: this RSS feed has an unreadable date, a missing link, a relative link, an Atom-style date and no closing </rss>.
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Example Blog</title>
<link>https://example.com/</link>
<description>Latest posts</description>
<atom:link rel="self" href="https://example.com/feed/"/>
<item>
<title>Second post</title>
<guid>https://example.com/2/</guid>
<pubDate>15/10/2026</pubDate>
</item>
<item>
<title>First post</title>
<link>/1/</link>
<guid>https://example.com/1/</guid>
<pubDate>2026-10-12T09:00:00+09:00</pubDate>
</item>
</channel>
The validator reports these five findings, in this order (Errors first, then by line):
The date cannot be parsed, so readers cannot order the items.
15/10/2026An element is never closed. Readers abort parsing here, so items may not appear at all.
<rss>The item has no <link>.
The link is relative. Feeds are read on other domains, so links must be absolute.
/1/The date format does not match the spec (RSS 2.0 wants RFC 822 — "Fri, 18 Sep 2026 09:00:00 +0900"; Atom wants RFC 3339 — "2026-09-18T09:00:00+09:00"). Many readers cope, but matching the spec is safer.
2026-10-12T09:00:00+09:00date_invalid (Error): 15/10/2026 cannot be read as a date at all, so readers cannot place the item. The line number points to the item that holds the date.unclosed_tag (Error): the element named in the finding — here <rss> — is never closed; add </rss> at the end. For this finding the line number can point past the opening tag, so go by the element name.item_missing_link / item_relative_link (Warning): the first item has no <link>, and the second uses /1/. Feeds are read on other domains, so write the full https://example.com/1/. We only look at what the feed says; we do not open the article to see whether it exists.date_wrong_format (Warning): the value is a valid date, but in Atom form. RSS 2.0 expects Mon, 12 Oct 2026 09:00:00 +0900.XML syntax: unclosed and mismatched tags, bare & and <, unterminated comments and CDATA, duplicate roots, text outside the root, <!DOCTYPE> (never expanded)
Feed shape: format (RSS 2.0 / RSS 1.0 / Atom), required elements (RSS: channel title, link, description; Atom: feed title, id, updated), rel="self", empty feeds
Items: title, link (absolute?), <guid> / <id> (missing, duplicated), dates (format, timezone, weekday, future), reverse-chronological order
Encoding: XML encoding vs HTTP charset, undecodable bytes, byte patterns typical of mojibake, BOM
Podcast: itunes:image, itunes:category, itunes:explicit, language, <enclosure> (url, type, length), <guid>, <pubDate>, itunes:duration, owner email
Errors are at the "readers cannot parse this" level — a bare &, an unclosed tag, a missing required element. Warnings are "most readers cope, but it violates the spec", and FYI items are "works either way, safer if aligned".
The correct date format depends on the spec. RSS 2.0 wants RFC 822 (Fri, 18 Sep 2026 09:00:00 +0900); Atom wants RFC 3339 (2026-09-18T09:00:00+09:00). We warn when a parseable value still has the wrong shape.
Mojibake is a mismatch between what you declare and what you send. We compare the XML declaration's encoding, the HTTP charset and the actual bytes, and report undecodable bytes and byte patterns typical of mojibake.
Even when a <!DOCTYPE> is present, entities are never expanded (no external entity references, no entity expansion). We only report that the declaration exists.
We validate a single feed. Links are not followed and article bodies are not checked (broken links belong to the Broken Link & Alt Checker).
Sent: only the URL you type. Our server (Cloudflare Workers) fetches it with a public GET. If the URL returns HTML we additionally fetch just the first few KB of up to six discovered candidates, then fetch the one confirmed to be a feed. No cookies, no credentials.
Stored: nothing. The validation lives entirely inside one request and writes neither the URL nor the result to any database (no KV, no D1). It leaves memory when the request ends.
Returned: the findings plus truncated titles, summaries, dates and links. The fetched feed is never re-served — this app cannot be used as a proxy for other sites. Item HTML is stripped of tags and cut to 200 characters before display.
Limits: 5 redirects, 10 second timeout, 512KB read limit for the feed and 4KB for each candidate check. Anything beyond that is not read.
Note: public GET only. URLs pointing at internal networks, localhost or private IPs are rejected.
POST /feed-check/api/check with JSON ({"url": "https://example.com/feed/"}) or a form body and you get the same result back as JSON.