RSS в 66 социальных сетей: Facebook, Instagram, X, LinkedIn, Telegram и другие Блог Партнерская программа Контакты
Войти Начать бесплатно
Updated: 2026-10-06
Go RSS Feed with gorilla/feeds: Serve It and Auto-Post Items

Short answer: A Go RSS feed is one HTTP handler. Use the gorilla/feeds package to build a feeds.Feed from your latest published posts with absolute links, stable IDs and UTC times, call ToRss(), and serve the result with the application/rss+xml content type through http.ServeContent so conditional requests work. Cache it for a few minutes, validate it once, and any RSS auto-poster can share every new article automatically.

Go is popular for fast, low-maintenance web services: company sites, developer blogs, documentation portals, status pages and content APIs that run for years on a single binary. The standard library gives you an excellent HTTP server and XML encoder, but no ready-made feed. Adding one is worth the hour it takes. A feed is how readers subscribe, how newsletters and aggregators find your newest content, and how social automation tools publish each article the moment it goes live. This guide shows a reliable way to build the feed in Go, the details that most often break it, and how to connect it to social auto-posting.

What a Go RSS feed has to deliver

Agree on the requirements first. A dependable feed:

  • lists the most recent public items, newest first, usually 20 to 50;
  • gives each item a title, an absolute link, a summary, a publication date and a permanent unique ID;
  • describes the channel with a title, a link to the site and a description;
  • is served as RSS or XML in UTF-8, with no redirects;
  • stays stable over time, so tools never treat an old item as new.

Images and categories are optional, but they shape the social posts built from the feed, so we add them once the basic version validates.

Option one: gorilla/feeds

The gorilla/feeds package builds RSS 2.0, Atom and JSON Feed from one set of Go structs. It handles XML escaping and date formatting, which are the usual weak points of hand-built feeds. Add it to your module:

go get github.com/gorilla/feeds

A handler that builds the feed looks like this. It assumes a function latestPosts(ctx, 30) that returns published posts whose publication time has passed, newest first, with Title, Slug, Summary и PublishedAt fields. The public base URL comes from configuration.

package main

import (
    "bytes"
    "net/http"
    "time"

    "github.com/gorilla/feeds"
)

const baseURL = "https://example.com"

func rssHandler(w http.ResponseWriter, r *http.Request) {
    posts, err := latestPosts(r.Context(), 30)
    if err != nil {
        http.Error(w, "feed unavailable", http.StatusInternalServerError)
        return
    }

    updated := time.Now().UTC()
    if len(posts) > 0 {
        updated = posts[0].PublishedAt.UTC()
    }

    feed := &feeds.Feed{
        Title:       "Example Blog",
        Link:        &feeds.Link{Href: baseURL + "/"},
        Description: "New articles from Example",
        Created:     updated,
    }

    for _, p := range posts {
        url := baseURL + "/blog/" + p.Slug
        feed.Items = append(feed.Items, &feeds.Item{
            Title:       p.Title,
            Link:        &feeds.Link{Href: url},
            Id:          url,
            Description: p.Summary,
            Created:     p.PublishedAt.UTC(),
        })
    }

    rss, err := feed.ToRss()
    if err != nil {
        http.Error(w, "feed unavailable", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/rss+xml; charset=utf-8")
    w.Header().Set("Cache-Control", "public, max-age=300")
    http.ServeContent(w, r, "rss.xml", updated, bytes.NewReader([]byte(rss)))
}

Several choices matter. The item Id is the permanent article URL and becomes the item’s guid; it should never change after publication. The feed’s modification time follows the newest post rather than the current time, so it does not look changed on every request. Errors stop the handler before any XML is written, so a failed query never produces a half-finished document. And http.ServeContent takes care of If-Modified-Since requests, answering 304 Not Modified when nothing is new.

Option two: encoding/xml with your own structs

If you prefer no dependency, the standard encoding/xml package is enough. Define structs for the channel and items with XML tags such as xml:"title" и xml:"guid", marshal them with xml.MarshalIndent, and write xml.Header before the result so the document starts with the XML declaration. The encoder escapes text automatically.

The one thing to do by hand is the date. Format publication times with t.UTC().Format(time.RFC1123Z), which produces the numeric-offset form RSS readers accept. Do not use time.RFC3339 in an RSS feed; that format belongs to Atom. The standard library approach gives you full control over unusual elements; gorilla/feeds is quicker when you want RSS and Atom from the same data. The net/http documentation for ServeContent explains how either version can serve conditional responses.

Dates, time zones and encoding

Go is strict about types, but feeds still go wrong in predictable places.

  • Time zones. A time.Time read from the database may carry the server’s local zone or no meaningful zone at all, depending on the driver settings. Normalise with .UTC() before building items, and configure the driver to parse times, for example with parseTime=true&loc=UTC for MySQL.
  • UTF-8. Go strings are UTF-8 by convention, and the content type declares it. Problems usually come from data imported in another encoding or from invisible control characters pasted from word processors, which are invalid in XML.
  • HTML in summaries. Decide deliberately whether a summary is plain text or HTML. Plain text produces cleaner social captions; full HTML belongs in the item’s content field.

Our guides to RSS date format errors и time zones and daylight saving in RSS auto-posting cover these problems in more depth.

Absolute URLs behind a proxy

The example uses a fixed baseURL, which is the safest option. Go’s HTTP server does not rewrite requests based on proxy headers, so if you build links from r.Host и r.TLS behind Nginx, Caddy or a load balancer, you will often get the internal host or plain HTTP.

  • Configure the base URL. Read it from an environment variable or a config file, and use the same value in feeds, sitemaps and emails.
  • If you must use headers, read X-Forwarded-Proto и X-Forwarded-Host only when the request really comes from your proxy.
  • Check the live output. Search the public feed for http://, localhost, :8080 and internal host names.

Relative links cause the same trouble from the other direction: they work in templates but break once an item is read elsewhere, as explained in our article on relative URLs and broken links in RSS feeds.

Images, categories and discovery

Richer items produce better social posts. When the basic feed validates, add:

  • An image per item. gorilla/feeds items have an Enclosure field for a URL, length and MIME type. Use an absolute URL to a mid-sized picture; many auto-posting tools use it as the post image.
  • Categories. Tools can turn categories into hashtags or filter by topic. With your own structs, add one category element per tag.
  • Open Graph tags. Give every article page og:title, og:description и og:image, because tools often fall back to them.
  • Autodiscovery. In your html/template layout, add <link rel="alternate" type="application/rss+xml" title="Example Blog" href="https://example.com/rss.xml"> so browsers and tools can find the feed.

Atom, JSON Feed and category feeds

Because one feeds.Feed value can be written as RSS, Atom or JSON Feed, extra formats are almost free. Move the building code into a function such as buildFeed(posts, title), then add handlers for /atom.xml using ToAtom() with application/atom+xml, and /feed.json using ToJSON() if you want it.

Category feeds use the same function. With the Go 1.22 router you can register mux.HandleFunc("GET /category/{slug}/rss.xml", ...) and read the slug with r.PathValue("slug"). Return 404 for unknown categories, give each feed a descriptive title, and keep every article’s ID identical across feeds so tools that follow several feeds see one item. If duplicates still appear, the guide on duplicate GUID fixes explains how auto-posters decide what is new.

Caching the generated feed

Feeds are polled constantly by readers, aggregators and auto-posting services, often every few minutes. Go is fast, but each request still means a database query.

  • Cache-Control. A public max-age of a few minutes, as in the example, lets a reverse proxy or CDN answer most requests.
  • Conditional responses. http.ServeContent already handles If-Modified-Since. You can also set an ETag header before calling it, and it will honour If-None-Match as well.
  • In-process cache. Keep the generated bytes and their build time in a struct guarded by a sync.RWMutex, rebuild when they are older than a few minutes, and reset the cache when a post is published.
  • Small payload. Thirty items with summaries is enough for subscribers and automation.

Long cache lifetimes delay auto-posting, so keep them short. Our explanation of why cached feeds make auto-posts lag shows how the delays add up.

Test and troubleshoot

A table-driven test with httptest.NewRecorder protects the feed. Call the handler with a request for /rss.xml, check status 200 and the content type, unmarshal the body with encoding/xml into a small struct and assert that the first item has a title, an absolute link and a parsable pubDate. Run it with go test in CI so a refactor cannot silently break the feed.

ProblemTypical cause in GoFix
Links use http or an internal hostURLs built from r.Host behind a proxyUse a configured base URL
Dates shifted by hoursDriver returns local or zone-less timesParse as UTC and call .UTC()
Validator rejects pubDateRFC3339 used in an RSS feedFormat with time.RFC1123Z
Missing XML declarationxml.Marshal without xml.HeaderWrite xml.Header first
Same post shared twiceItem ID built from an editable titleUse the permanent URL as the ID
Feed always looks changedModification time set to time.Now()Use the newest post’s time

Before connecting automation, fetch the headers with curl -I, expect HTTP 200 without redirects, validate the feed and open a few item links in a private window to confirm they are public.

Connect the feed to social auto-posting

With a valid feed in place, sharing no longer depends on someone remembering to post. Add the feed URL to an auto-posting service, connect your social accounts and channels, decide how posts are built from the title, summary and link, and choose posting hours if needed. The service checks the feed and publishes each new item once.

Go teams often route content by audience: engineering posts to Mastodon or Bluesky, product updates to a professional network, release notes and incident updates to a Telegram channel, a Discord server or a team chat. Category feeds make each route a matter of choosing the right feed. More setups are collected under technical guides and the RSS-лента topic.

How PostRSS works with a Go feed

PostRSS reads any RSS or Atom feed, including one served by a Go handler, and publishes each new item to the networks you connect. It checks feeds as often as every minute, takes the image from the feed item or the page’s Open Graph tags, and offers keyword filters, hashtags from item categories and UTM parameters. Developers can also use the PostRSS REST API v1 and outgoing webhooks, and the service has been running since 2014. See the features page for all 66 networks and the pricing page for what each plan includes.

Related reading

The bottom line

A Go RSS feed needs one handler and a few careful choices: gorilla/feeds or encoding/xml for safe XML, a configured HTTPS base URL, UTC times in the right format, permanent item IDs, the correct content type, early error returns and a short cache served through http.ServeContent. Add images and categories, offer Atom or JSON Feed from the same data if you like, cover the handler with an httptest check and validate it once. After that the feed runs quietly for years, and every new article reaches subscribers and your social channels without manual sharing.

FAQ

Does Go’s standard library support RSS?

There is no feed package in the standard library, but encoding/xml and net/http are enough to build one. The gorilla/feeds package makes it shorter.

Which date format should a Go RSS feed use?

Use an RFC 822 style date. In Go, format UTC times with time.RFC1123Z. RFC3339 is the format for Atom feeds, not RSS.

How do I return 304 Not Modified for my feed?

Serve the generated bytes with http.ServeContent and pass the newest post’s time. It answers If-Modified-Since requests, and an ETag header you set is honoured too.

Can one Go handler produce RSS and Atom?

Yes. A gorilla/feeds Feed value can be written with ToRss, ToAtom or ToJSON, so one builder function can serve several formats.

Can I auto-post new articles from a Go site to social media?

Yes. Once the feed is public and valid, an RSS auto-posting service can watch it and publish each new article to the social networks and channels you connect.

New guides, once a month

What changed in the networks, what broke, and how to fix it before it costs you reach.

We send a confirmation e-mail first. Unsubscribe any time.

Другие инструменты нашей команды

Создано Internet Solutions — командой PostRSS. Каждый продукт экономит ваше время по-своему.

ИИ-чат для сайтов Talkmio Ваш сайт отвечает посетителям круглосуточно на основе вашего контента и на их языке. Бесплатный план · без карты ИИ-ассистент Ask Mio Чат, код, дизайн, тексты и исследования. Mio выбирает лучшую модель для каждой задачи. Бесплатный план ИИ-автопилот для блога и соцсетей AI Blog Autopilot ИИ пишет SEO-статьи на 2 000–3 000 слов и публикует каждую в 58+ соцсетях. Первые 3 статьи бесплатно Проверка состояния сайта Site AI Audit SEO, скорость, SSL, безопасность и настройки почты в одном отчёте — в порядке важности исправлений. Первый аудит бесплатно Глубокий SEO-аудит Site SEO AI Audit Полный SEO-обход по 7 направлениям, включая видимость в ИИ-поиске, с исправлениями по степени влияния. Первый аудит бесплатно RSS и товарные фиды RSS Feed Creator Создавайте RSS из любой веб-страницы, а также товарные фиды для Google и Meta, которые обновляются сами. Бесплатный план Разработка сайтов и SEO Internet Solutions Сайты, интернет-магазины и индивидуальные системы — проектируем, создаём и поддерживаем сами. С 2011 года
PostRSS — платформа автоматизации RSS-лент и автопостинга
Обзор конфиденциальности

Этот сайт использует файлы cookie, чтобы обеспечить вам наилучший пользовательский опыт. Информация о cookie хранится в вашем браузере и выполняет такие функции, как распознавание вас при повторном посещении сайта, а также помогает нашей команде понять, какие разделы сайта являются для вас наиболее интересными и полезными.