
Short answer: Apache can serve an RSS feed correctly with a handful of lines in the main configuration or in an .htaccess file. Set the RSS content type with a UTF-8 charset, compress the feed types with mod_deflate, give the feed a short cache lifetime with mod_expires, keep the file validators that Apache adds to static files and redirect old feed addresses with a permanent status. Make sure that no rewrite rule, firewall module or rate limit blocks the tools that poll the address, then test with curl and connect the feed to an auto-poster.
Apache still runs a large share of small business sites, shared hosting accounts and WordPress installations, and many of those sites serve a feed without anyone having looked at the headers. The result is a feed that works in a browser and fails in a stricter tool: a plain XML type instead of the RSS type, no compression, a cache lifetime of a day, or a redirect chain from an old address. This guide shows a working set of directives for a static feed and for a feed that comes from an application, explains what each line does and lists the problems that appear after an upgrade or a move to a new host. Always run the configuration test before you reload the server, because a typo can stop the whole site.
Apache reads settings from two places. The main configuration, usually a virtual host file, is read once when the server starts or reloads, and it is the better place for feed rules because it is faster and cannot be changed by accident. An .htaccess file in a folder is read on every request and works only when the virtual host allows overrides. Shared hosting almost always uses .htaccess, a server you manage yourself should use the virtual host. The directives below work in both places, with one condition: the modules must be enabled and, for .htaccess, the override level must permit them.
| Need | Module | Override level for .htaccess |
|---|---|---|
| Content type and charset | mod_mime, mod_headers | FileInfo |
| Compression | mod_deflate, mod_filter | Options or FileInfo |
| Cache lifetime | mod_expires, mod_headers | Indexes |
| Redirects | mod_alias, mod_rewrite | FileInfo |
| Caching an application feed | mod_cache, mod_cache_disk, mod_proxy | Main configuration only |
If a directive has no effect, the first suspect is a missing module or a restricted override level. On Debian and Ubuntu you enable a module with a command and then reload; on other systems you uncomment a line in the main file. The server error log names the directive that was refused, which saves a lot of guessing.
Feed generators often write a file called feed.xml, and Apache’s type table maps the xml extension to application/xml. The RSS type is a clearer signal for readers and validators, and a charset label removes any doubt about how to decode the text. Set both for the feed file only, so that the rest of the site is untouched.
<IfModule mod_headers.c>
<Files "feed.xml">
Header set Content-Type "application/rss+xml; charset=utf-8"
</Files>
</IfModule>
AddType application/rss+xml .rss
AddType application/atom+xml .atomThe conditional wrapper keeps the site working if the headers module is not loaded. The two type lines cover feeds with the rss and atom extensions, which Apache would otherwise serve with a generic type. The meaning of the header and the types that tools accept are explained in the guide to the content type header.
An XML feed is repetitive text, and it compresses to a fraction of its size. Apache’s deflate module does not compress feed types unless you list them.
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/rss+xml application/atom+xml text/xml application/xml
</IfModule>The module adds a Vary header for the encoding, so that shared caches do not hand a compressed answer to a client that cannot read it. A side effect is worth knowing: when Apache compresses a response on the fly, it adds a suffix to the ETag, which is correct and keeps conditional requests working in current versions. Compression helps most when many tools poll the feed, and the details are covered in the feed compression guide.
A feed that changes a few times a day should not be cached for a day. If it is, a new article appears in readers and auto-posters hours after you publish it. Five minutes is a common compromise, because it protects the server from bursts and still lets a new article travel quickly.
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType application/rss+xml "access plus 5 minutes"
ExpiresByType application/atom+xml "access plus 5 minutes"
</IfModule>The module writes both an Expires header and a Cache-Control header with a matching maximum age. If your site already has a rule for all text files that sets a long lifetime, make sure the feed types are listed above it or that the long rule does not match them. A broad rule such as one hour for every XML file is a common reason for late posts, and the background is explained in why auto-posted content sometimes lags.
For static files, Apache adds an ETag and a Last-Modified header automatically, and it answers a conditional request with a 304 status and no body. This is the best reason to keep a static feed when your content does not change every minute: the server does almost no work for tools that ask whether anything has changed. If you changed the ETag setting in the past, check that the default still builds the tag from the modification time and the size, because a tag based on the file number can differ between servers behind a load balancer and cause needless downloads.
FileETag MTime SizeIf a script regenerates the file, write it to a temporary name and rename it into place, so that no request reads half a document. How the validators work for tools that poll is described in the guide to ETags and conditional requests.
Keep one canonical address and use it everywhere: in the link tags of your pages, in your notes and in the auto-poster. If older addresses must keep working, send them to the canonical one with a permanent redirect.
Redirect 301 /rss /feed.xml
Redirect 301 /feed /feed.xml
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^feed\.xml$ https://example.com/feed.xml [R=301,L]The first two lines use the alias module, which is simple and fast. The last block sends a plain HTTP request for the feed to HTTPS and needs the rewrite module. Tools follow redirects, but every redirect costs a request, and a chain of two or three is a typical reason for a feed that works in a browser and fails elsewhere. Publish the HTTPS address in the first place, so that no tool starts with an insecure request. The meaning of each status code is explained in the guide to feed status codes.
Content management systems install their own rewrite rules in .htaccess. A rule that sends every request for a missing file to index.php is useful for pretty addresses, and it is also the reason a feed file that you forgot to upload returns an HTML page with a 200 status instead of a 404. Tools then try to parse a web page as a feed and report a confusing error. If you serve a static feed beside a CMS, put your feed rules above the CMS block, and add a condition that lets real files through. After any change, request the address with curl and look at the status line and the content type, not only at the page in your browser.
When the feed is produced by PHP, Node or another application behind Apache, you can store the answer for a few minutes and protect the application from repeated requests. The disk cache module does this in the main configuration.
CacheRoot /var/cache/apache2/mod_cache_disk
CacheQuickHandler Off
<Location "/feed.xml">
CacheEnable disk
CacheDefaultExpire 300
CacheLock on
CacheStaleOnError on
ProxyPass "http://127.0.0.1:3000/feed.xml"
ProxyPassReverse "http://127.0.0.1:3000/feed.xml"
</Location>A stored answer lives for five minutes, the lock makes simultaneous requests wait for one refresh instead of calling the application fifty times, and the stale option lets Apache return the last good copy when the application returns an error. The default lifetime is used only when the application sends no expiry of its own, so it is better to have the application send a short Cache-Control header as well. Background on slow upstream servers is in the guide to feed timeouts.
Protection modules and firewall plugins are the second most common cause of a feed that works for you and fails for others. A rule that blocks empty or unknown user agents, a limit on requests per minute that was tuned for browsers, or a country filter can all answer an auto-poster with a forbidden or a too-many-requests status. Give the feed path an exception in each protection layer: the web server module, a firewall plugin in the CMS and any service in front of the server. If a tool reports that the feed is unreachable, check these settings before you look anywhere else. Remember that some hosts add their own filters that you cannot see in your files, and a message to their support with the exact request and the time is often the fastest way to find them.
The access log tells you 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 close to 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. A separate log file for the feed location makes this much easier on a busy site, because the feed lines are not mixed with page views.
Check the syntax before every reload and then test from outside the server.
sudo apachectl configtest && sudo systemctl reload apache2
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.
Apache needs only a few lines to serve a dependable feed. Set the RSS content type and a UTF-8 charset for the feed file, compress the feed types, give the feed a lifetime of a few minutes, keep the validators that static files bring, redirect old addresses straight to the final one and make sure that no rewrite rule or protection module blocks the readers. 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. Apache maps the xml extension to a generic XML type, so set the RSS type for the feed file with a Files block and the headers module.
Yes, for the content type, compression, expiry and redirects, as long as the host allows overrides and loads the modules. Caching an application feed needs the main configuration.
A few minutes is a good compromise. A lifetime of hours makes new posts reach your social channels late.
Usually a firewall rule, a bot filter or a rate limit is blocking it. Give the feed path an exception in every protection layer.
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.
Creadas por Internet Solutions, el equipo detrás de PostRSS. Cada una te ahorra tiempo a su manera.