ScreenshotNeo

BlogHow-to

How to Create an RSS Feed for a Website

Find your existing feed or build a valid RSS 2.0 feed from your content, publish it, and keep it useful to readers and search engines.

By the ScreenshotNeo team4 October 20269 min read

Direct answer: First check whether your CMS already publishes an RSS or Atom feed. If it does, use and link to that feed. If it does not, generate an RSS 2.0 XML document from your published content, serve it at a stable public URL, and update it when content changes. An RSS feed reports recent updates; use an XML sitemap when you need to describe a broader set of site URLs.

1. Check whether your website already has a feed

Most CMS platforms generate feeds automatically. Try common feed paths such as /feed, /rss, or /feed.xml, and check your CMS documentation or settings. A feed should return XML rather than a normal HTML page.

For WordPress, the RSS 2.0 feed is commonly available at https://example.com/feed/, and Atom at https://example.com/feed/atom/. WordPress documents the bloginfo('rss2_url') and bloginfo('atom_url') template tags for obtaining the corresponding URLs. See WordPress Feeds and the bloginfo() reference.

<a href="<?php bloginfo('rss2_url'); ?>">RSS</a>
<a href="<?php bloginfo('atom_url'); ?>">Atom</a>

Replace the example domain with your site when checking manually. If a feed exists, inspect its contents and make sure it includes the posts you intend to publish. Avoid building a second feed until you know why the generated one is insufficient.

2. Understand the RSS 2.0 structure

An RSS 2.0 document has an <rss> root, a <channel> describing the publication, and one or more <item> elements describing updates. The channel requires title, link, and description. Item fields have permitted combinations; in a typical website feed, give each item a title, a canonical link, and a stable GUID. The RSS 2.0 specification describes the format and item field rules.

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Example Site</title>
    <link>https://example.com/</link>
    <description>Recent updates from Example Site</description>
    <item>
      <title>Example article</title>
      <link>https://example.com/example-article/</link>
      <description>A short summary of the article.</description>
      <pubDate>Tue, 04 Oct 2026 12:00:00 +0000</pubDate>
      <guid isPermaLink="true">https://example.com/example-article/</guid>
    </item>
  </channel>
</rss>

The date above is illustrative; never copy it into a live feed. Use the actual publication or meaningful update time. If an item has a stable URL, using that URL as its GUID is a straightforward choice. Keep the GUID stable when the article is edited so feed readers do not mistake an edit for a new post.

3. Generate a feed in Python

This small runnable example uses only the Python standard library. It exposes /feed.xml on port 8000 and creates XML from a list of content records. Replace the sample records with records from your database or publishing API. For production, connect this handler to your existing application server and data layer rather than keeping content in source code.

from datetime import datetime, timezone
from email.utils import format_datetime
from http.server import BaseHTTPRequestHandler, HTTPServer
from xml.etree.ElementTree import Element, SubElement, tostring

SITE_URL = "https://example.com/"
POSTS = [
    {
        "title": "Example article",
        "url": "https://example.com/example-article/",
        "summary": "A short summary of the article.",
        "published": datetime(2026, 10, 4, 12, 0, tzinfo=timezone.utc),
    },
]


def make_feed():
    rss = Element("rss", {"version": "2.0"})
    channel = SubElement(rss, "channel")
    SubElement(channel, "title").text = "Example Site"
    SubElement(channel, "link").text = SITE_URL
    SubElement(channel, "description").text = "Recent updates from Example Site"

    # Keep a bounded list of recent published posts, newest first.
    for post in sorted(POSTS, key=lambda p: p["published"], reverse=True)[:20]:
        item = SubElement(channel, "item")
        SubElement(item, "title").text = post["title"]
        SubElement(item, "link").text = post["url"]
        SubElement(item, "description").text = post["summary"]
        SubElement(item, "pubDate").text = format_datetime(post["published"])
        guid = SubElement(item, "guid", {"isPermaLink": "true"})
        guid.text = post["url"]

    return b'<?xml version="1.0" encoding="UTF-8"?>\n' + tostring(rss, encoding="utf-8")


class FeedHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path != "/feed.xml":
            self.send_error(404)
            return
        body = make_feed()
        self.send_response(200)
        self.send_header("Content-Type", "application/rss+xml; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.send_header("Cache-Control", "public, max-age=300")
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, format, *args):
        pass


if __name__ == "__main__":
    HTTPServer(("0.0.0.0", 8000), FeedHandler).serve_forever()

Save this as feed_server.py and run python3 feed_server.py. It serves http://localhost:8000/feed.xml. Before deploying, change SITE_URL and the sample post URLs to your real canonical HTTPS URLs. The example’s sample timestamp is data for the demonstration; production records should supply the actual dates.

The XML library escapes characters such as ampersands and angle brackets in element text. That matters for titles and summaries such as “Research & development”: raw ampersands can make XML malformed. If you build XML by concatenating strings, escape every value with an XML-aware escaping function. Google’s documentation also calls out entity escaping for XML values.

  1. Choose a stable, public URL such as https://example.com/feed.xml. Return an XML document with an appropriate content type, such as application/rss+xml; charset=utf-8.
  2. Link to it from a visible place on your site, such as a feed link in the footer or publication page. In the page head, you can also advertise it to feed readers with <link rel="alternate" type="application/rss+xml" title="Example Site RSS" href="https://example.com/feed.xml">.
  3. Ensure the feed and each linked article are publicly fetchable. Use canonical article URLs, not session-dependent URLs, internal preview links, or URLs blocked from access.
  4. If you move the feed, keep the old URL working or redirect it to the new feed where practical. Subscribers may have saved the original address.

If you offer multiple formats, label each link clearly (for example, RSS 2.0 and Atom) so readers know what they are selecting.

5. Keep the feed current and accurate

  • Include recent entries. A feed is usually a rolling window of the latest published items, rather than a complete archive. Pick a reasonable window for your publishing cadence and consumer needs.
  • Use the right date. RSS pubDate uses an RFC 822 style date. Emit a timezone, preferably UTC, and update the timestamp only when the content is actually published or meaningfully changed. Do not set it to the time the feed is requested.
  • Keep item identity stable. GUIDs should remain the same for the same item across feed refreshes. Do not generate random GUIDs on every request.
  • Use canonical, fetchable links. Each item should point to the public canonical article. Avoid duplicate URLs, redirects that loop, and links to unpublished drafts.
  • Return a clean response. Do not prepend debug output, warnings, HTML, a byte-order mark, or server error text before the XML declaration.

Google’s guidance recommends keeping feed information current and using meaningful modification dates. A feed should reflect real content changes rather than a request timestamp. See Google’s feed and sitemap best practices.

6. RSS feed or XML sitemap?

Need Use
Report recent posts or updates to subscribers and crawlers RSS or Atom feed
Describe a wider inventory of URLs for search engines XML sitemap
Both new updates and broad URL coverage Maintain both

Google accepts RSS 2.0 and Atom feeds as sitemap formats, but a feed includes only recent URLs. It does not replace a sitemap intended to enumerate a site’s pages, and submitting a feed does not guarantee indexing. Google’s current Build and Submit a Sitemap documentation explains that most CMS platforms automatically generate RSS or Atom feeds.

7. Check the feed with cURL

Fetch the feed and inspect its response headers and body. Replace the URL with your public feed endpoint.

curl -i https://example.com/feed.xml

# Save the response body for inspection
curl -fsS https://example.com/feed.xml -o feed.xml

Check that the status is successful, the content type identifies XML, and the body starts with the RSS root rather than an HTML page. Then parse the saved file with an XML parser or open it in a feed reader. A successful HTTP response alone does not prove the document is valid RSS.

8. Troubleshooting

Symptom Likely cause Fix
Feed URL returns 404 The CMS uses another endpoint, rewrites are not configured, or the route is missing. Check the CMS feed documentation and permalink settings. Test the generated feed URL directly, then configure a stable route.
Browser displays a website page instead of XML The URL points to a page, or an error handler is serving HTML with a success status. Inspect the response with cURL, confirm the route, and ensure the handler returns the feed body and XML content type.
XML parser reports an ampersand or invalid token A title or description contains unescaped XML characters, or stray output precedes the document. Use an XML serializer, escape values, and remove debug output, warnings, and byte-order marks.
Feed reader shows duplicate posts on every refresh Item GUIDs change between requests or do not uniquely identify posts. Use a stable GUID derived from the permanent content identity and keep it consistent.
Dates look wrong or updates are missed Dates omit a timezone, use the request time, or are parsed incorrectly. Emit valid RSS date values with a timezone and store the actual publication or meaningful modification time.
Feed has no new posts The CMS query filters them out, the cache is stale, or the items are drafts/unpublished. Check the content query and publication state, then review cache TTL and purge stale cached output after publishing.
Google does not index a URL from the feed Feed submission is a discovery signal, not an indexing guarantee; the URL may also be inaccessible or duplicate. Check that the page is fetchable and canonical, and maintain an XML sitemap for broader coverage.

9. Performance, reliability, and cost

For a small feed, generating XML on each request is usually straightforward. As traffic or content volume grows, cache the serialized feed and invalidate or refresh it when a post is published or meaningfully edited. Keep the feed bounded to recent entries, use a database index or CMS query for the newest published records, and avoid fetching each article page during feed generation.

Feeds are small compared with page assets, but reliable delivery still depends on your site and hosting. Monitor the feed route for server errors, ensure caches do not keep outdated entries longer than intended, and preserve the feed URL when migrating. RSS itself has no per-request licensing fee; hosting and any external infrastructure costs depend on your existing stack. Do not use a screenshot capture to validate XML: inspect and parse the response body directly.

Or skip the browser setup

If your next step is documenting or previewing how the feed endpoint appears in a browser, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It does not create or validate RSS; use it to capture the feed page or related web pages. One GET request returns an image or PDF. See the ScreenshotNeo documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners, popups, and chat widgets are removed before the screenshot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, no card required.

FAQ

Does every website need an RSS feed?

No. It is useful when readers or tools need a machine-readable stream of updates. A site that has no regularly updated content may have little to publish.

Can a feed contain full article text?

Yes. RSS item descriptions can contain summary or full content, subject to the feed format and the needs of your readers. Choose deliberately: full text can make reading convenient, while a summary directs readers to the canonical page.

Can I submit an RSS feed to Google?

Google accepts RSS 2.0 and Atom feeds as sitemap formats for recent URLs. Maintain an XML sitemap when you need broader URL coverage, and remember that submission does not guarantee indexing.

Should I publish RSS or Atom?

Either can serve updates. If your CMS provides both, you can expose both with clear labels; if you are implementing a single feed, RSS 2.0 is widely understood and has a concise specification.