
Short answer: Nginx can serve your RSS feed correctly in a few lines of configuration. Map the feed to the RSS content type with a UTF-8 charset, because the default type table serves .xml files as plain XML. Turn on compression for feed types, keep the built-in ETag and Last-Modified validators, add a short cache lifetime and, when the feed comes from an application, put a small proxy cache with stale fallback in front of it. Do not let rate limits or bot rules block the tools that poll the address, then test the live feed with curl and connect it to an auto-poster.
Most feeds pass through Nginx on the way to readers, whether it serves a static file or sits in front of a CMS, a Node service or a PHP application. That makes the web server the place where many feed problems start or end: a wrong content type, a missing compression rule, a cache that holds an old copy for an hour, a rate limit that answers an auto-poster with an error. This guide shows a working configuration for a static feed and for a proxied feed, explains each directive in plain language and lists the traps that are easy to meet after a server upgrade. Directive names are stable across current versions, but always run the configuration test before you reload.
If a script writes a file such as feed.xml into your web root, Nginx can serve it without any other software. The first surprise is the content type. The default types table maps the xml extension to text/xml, and the rss extension to application/rss+xml. Many generators write feed.xml, so the server announces plain XML. Most tools accept it, but the RSS type is the clearer signal, and some validators warn about the mismatch. A single location block fixes it.
server {
listen 443 ssl;
http2 on;
server_name example.com;
root /var/www/example.com/public;
location = /feed.xml {
default_type application/rss+xml;
charset utf-8;
expires 5m;
add_header Cache-Control "public";
try_files $uri =404;
}
}The exact-match location applies only to this address, so the rest of the site is untouched. The default type is used because the table has no better answer in this block, and the charset directive adds the UTF-8 label to the content type for the types in its list. The expires directive sets a lifetime of five minutes and also writes a matching cache header. The meaning of the header is explained in the guide to the content type header.
| Setting | What it does | Typical value |
|---|---|---|
| default_type | Content type when the table has no match | application/rss+xml |
| charset | Adds the character set to the header | utf-8 |
| expires | Sets a cache lifetime for clients | 5m |
| gzip_types | Lists the types that are compressed | Include the RSS and Atom types |
| proxy_cache_valid | Lifetime of a stored copy | 200 for 5m |
An XML feed compresses very well, often to a fraction of its size. Nginx compresses text and HTML by default when gzip is on, but not the feed types, so list them.
gzip on;
gzip_vary on;
gzip_min_length 512;
gzip_types application/rss+xml application/atom+xml text/xml application/xml;Put the lines in the http block so that they apply everywhere. The vary directive adds a header that tells caches that the answer depends on the encoding. Check the result with a request that accepts compressed data. Compression helps tools that poll often, and the details are covered in the feed compression guide. One side effect is worth knowing. When Nginx compresses an answer on the fly, it changes a strong ETag into a weak one, which is correct behaviour and does not break conditional requests.
For static files, Nginx adds an ETag and a Last-Modified header on its own, and it answers a conditional request with a 304 status and no body. You do not need to enable anything, and that is the best reason to keep a static feed when your content does not change every minute. If a script rewrites the file, write it to a temporary name and rename it into place, so that a request never reads half a document. The mechanism is described in the guide to ETags and conditional requests.
When the feed is produced by a CMS or a service, Nginx can store the answer for a few minutes and protect the application from repeated requests. It can also keep serving an older copy when the application fails, which is exactly what a feed needs.
proxy_cache_path /var/cache/nginx/feed levels=1:2 keys_zone=feed:10m max_size=50m inactive=1h;
location = /feed.xml {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_cache feed;
proxy_cache_valid 200 5m;
proxy_cache_lock on;
proxy_cache_background_update on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status always;
}A stored answer lives for five minutes. While it is being refreshed, the lock makes other requests wait and the stale option lets them receive the old copy, so a burst of polling tools causes one call to the application and not fifty. If the application returns an error or times out, visitors still get the last good feed. The extra header shows whether an answer was a hit, a miss or stale, which is useful when you test. Remove it later if you do not want to expose it. Background on delays is in why auto-posted content sometimes lags, and on slow servers in the guide to feed timeouts.
Headers set with add_header at one level replace the ones from the level above, they do not add to them. If your server block sets security headers and your feed location sets a cache header, the feed answer carries only the cache header. This is not harmful for a feed, but it surprises people who audit headers. If you need both, repeat the lines in the location or put them in a shared file that you include in both places.
Keep one canonical address for the feed and use it everywhere: in the page link tags, in the sitemap notes and in the auto-poster. If an older address must survive, redirect it with a permanent status.
location = /feed { return 301 /feed.xml; }
location = /rss { return 301 /feed.xml; }Tools follow redirects, but every redirect costs a request and can confuse validators, and a chain of two or three redirects is a common reason for failures. Also redirect plain HTTP to HTTPS at the server level, and make sure the feed address you publish already uses HTTPS, so that no tool starts with an insecure request. The meaning of each status is explained in the guide to feed status codes.
Sites that offer a feed for each category or language often use a pattern. A regular expression location can map every address that ends in .xml under a feed folder to the same rules, and a prefix location with the caret tilde modifier does the same for a folder without invoking the regular expression engine. Prefer the prefix form when you can, because it is easier to read and cannot match by accident. Whatever you choose, keep the list of feed addresses in one place and review it when a section is added or removed, so that no feed is left without the correct type, the compression rule and the cache settings.
Two protection features cause quiet feed outages. The first is rate limiting. A limit that is tuned for browsers can answer a polling tool with an error when several tools share an address, so give the feed its own location without a limit, or with a generous one. The second is bot filtering. Rules that block empty or unknown user agents, or that challenge requests from data centres, also block auto-posters and feed readers. If you use a firewall service in front of Nginx, add an exception for the feed path. When a tool reports that the feed is forbidden or unreachable, check these two settings before you look anywhere else.
The access log shows who polls the feed and how often. Filter it for the feed path and look at three things: the share of 304 answers, which should be high when validators work, the share of errors, which should be near zero, and the interval between requests from the same client. A client that asks every few seconds is misconfigured, and a client that never comes is probably blocked. If the log is large, add a separate log file for the feed location so that it is easy to read.
Always check the syntax before you reload, then test from outside the server.
sudo nginx -t && sudo systemctl reload nginx
curl -sI https://example.com/feed.xml
curl -sI -H 'Accept-Encoding: gzip' https://example.com/feed.xml
curl -sI -H 'If-None-Match: "paste-the-etag-here"' https://example.com/feed.xmlA longer routine is in the feed validation guide, and a shorter one in debugging a feed with curl.
PostRSS has been publishing RSS and Atom feeds to social networks since 2014. Paste the address of your feed, connect your accounts and choose how each post is built from the title, the description or the page title. PostRSS checks the feed as often as every minute and shares each new entry, with its image and link, to the channels you choose among 66 supported networks, messengers, team chats and blogs, including LinkedIn, X, Bluesky, Mastodon and Telegram. Keyword filters, UTM parameters and posting windows let you tune each channel. See the PostRSS features page and the plan limits on the pricing page. More developer guides are under the Technical Guide tag.
Nginx is a small part of your feed and a common source of trouble. Give the feed address its own location with the RSS content type and a UTF-8 charset, compress the feed types, keep the validators that static files bring, cache an application feed for a few minutes with a stale fallback and exempt the address from rate limits and bot rules. Test the syntax, test the headers from outside and then connect the address to an auto-poster, so that every article you publish reaches your social channels without surprises.
Use application/rss+xml with a UTF-8 charset. The default table serves .xml files as text/xml, so set the type in a location block for the feed.
For static files they are on by default. For a proxied feed, the application should send its own validator, or Nginx can store the answer for a few minutes.
A few minutes is a good compromise. A lifetime of hours makes new posts reach your social channels late.
Usually a bot rule or a rate limit is blocking it. Give the feed address an exception and check your firewall service as well.
Yes. PostRSS reads any valid RSS or Atom address that is reachable without a login. Paste the address, connect your channels and new articles are shared as soon as they appear.
What changed in the networks, what broke, and how to fix it before it costs you reach.
Από την Internet Solutions, την ομάδα πίσω από το PostRSS. Κάθε προϊόν σάς εξοικονομεί χρόνο με τον δικό του τρόπο.