If your PHP-FPM pool keeps maxing out and the access log is full of URLs ending in /feed/, the problem may not be visitors at all. Feed readers, aggregators and scrapers poll feeds around the clock, and every uncached hit boots WordPress.
A news site owner raised this on the WordPress.org forums this week. They counted about 104,500 RSS-related requests over a few days, and 76,000 of them went to individual post feeds like /post-slug/feed/. Tag feeds (17,600) and category feeds (5,200) made up most of the rest.
The post feeds are the easiest win. On a single post, /feed/ is the comments feed for that post, and almost nobody subscribes to it on purpose. Below I measure the load, cache what should stay, stop WordPress from advertising post comment feeds, and answer those requests before PHP runs.
Step 1. Count the feed requests in your access log
Confirm that feeds really are the problem before you block anything. The log location depends on the server: on Debian/Ubuntu it is usually /var/log/apache2/access.log or /var/log/nginx/access.log, and on shared hosting you download it from the control panel.
# How many requests hit a feed URL
grep -c '/feed' access.log
# Top 20 feed URLs
grep '/feed' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
# Top 20 user agents asking for feeds
grep '/feed' access.log | awk -F'"' '{print $6}' | sort | uniq -c | sort -rn | head -20
# Top 20 IPs asking for feeds
grep '/feed' access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Each output line is a count followed by the URL, user agent or IP. The awk fields assume the standard combined log format, so if the columns look wrong, open one log line and check where the URL and user agent sit.
If the top URLs are mostly /some-post/feed/ and /comments/feed/, keep reading. If one or two user agents or IPs are behind most of the traffic, you will also want Step 5.
Step 2. Make sure feeds are served from cache
A cached feed costs almost nothing. In the forum thread the moderator pointed out that page caching plugins like WP Super Cache, WP Rocket and W3 Total Cache can cache feeds, and that LiteSpeed Cache or nginx FastCGI cache do it at the server level. Some setups exclude feeds, so look for a feed option in your cache settings.
Then request the same feed twice and compare the headers:
curl -sI https://example.com/feed/
curl -sI https://example.com/feed/
On the second request you should see a cache hit header from your plugin or server. The header name differs per cache. If neither response carries a cache header, feeds are going straight to PHP.
Caching alone often fixes the load for the main, category and tag feeds. Post comment feeds are different: there is one per post, so on a large news site the cache is cold for most of them and bots still reach PHP.
Step 3. Stop advertising post comment feeds
On a single post that has comments open or already has comments, WordPress prints a <link rel="alternate"> tag pointing to that post’s comments feed. Crawlers pick it up from there. Since WordPress 6.1 the feed_links_extra_show_post_comments_feed filter turns off just that link.
Add it to your child theme’s functions.php or a small file in wp-content/mu-plugins/:
<?php
// Remove the per-post comments feed link from <head>
add_filter( 'feed_links_extra_show_post_comments_feed', '__return_false' );
// Optional: also remove the site-wide comments feed link (/comments/feed/)
add_filter( 'feed_links_show_comments_feed', '__return_false' );
This only removes the links from the page source. Bots that already know the URLs will keep asking for them, which is why the next step matters more.
Step 4. Redirect post feeds before WordPress loads
Instead of letting WordPress build an XML document for every request, send /post-slug/feed/ back to the post itself at the web server. Back up .htaccess first (here is how to find it if your FTP client hides it).
On Apache or LiteSpeed, add this above the # BEGIN WordPress block:
# Send single post comment feeds back to the post
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/(category|tag|author|comments)/
RewriteRule ^(.+)/feed/?$ /$1/ [R=301,L]
</IfModule>
The main /feed/ does not match the pattern, and the condition keeps category, tag, author and the site-wide comments feed working.
On nginx, put this inside the server block, above your location / block, then test and reload:
location ~ ^/(?!category/|tag/|author/|comments/)(.+)/feed/?$ {
return 301 /$1/;
}
sudo nginx -t && sudo systemctl reload nginx
nginx -t should report that the syntax is ok and the test is successful. If it prints an error, fix the line it names before reloading.
💡 If you use custom post types or taxonomies whose archive feeds you want to keep (for example /products/feed/), add their bases to the exclusion list, or those feeds get redirected too.
Now test both sides:
curl -sI https://example.com/hello-world/feed/ | head -5
curl -sI https://example.com/category/news/feed/ | head -5
The first should return a 301 with a Location header pointing to the post. The second should still return 200.
Why a 301 and not a 410? You can return 410 Gone instead (the [G] flag in Apache, return 410; in nginx), and it is just as cheap. The forum poster tried 410 and later saw a drop in Google traffic, although they could not tell whether the rule caused it. I prefer the 301 because a human who follows an old feed link still lands on the article.
Step 5. Block the scrapers that dominate the log
If Step 1 showed one or two user agents doing most of the damage, block them by name. Replace the example names with strings from your own log:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (BadBotName|AnotherScraper) [NC]
RewriteRule .* - [F,L]
</IfModule>
Be careful with broad patterns. Never block search engine crawlers or the feed services your readers actually use. A similar rule works for referrers, see blocking visitors coming from a specific website, and there are more rules in my .htaccess examples post.
When the traffic comes from many IPs, user agent blocks do little. A rate limiting or bot rule at Cloudflare (or another proxy) on paths containing /feed/ stops it before it reaches your server.
If your server runs OpenPanel, you do not need SSH for Steps 1 and 5. The Domain Logs page shows raw access logs with the client IP, status code, URI and duration of each request (the server admin has to enable the module), and the IP Blocker takes IPs or CIDR ranges one per line. cPanel has the same two tools under Raw Access and IP Blocker, but there you usually download the raw log archive and read it locally.
Keep the feeds people actually use
Do not disable /feed/, category or tag feeds just to cut load. Feed readers, aggregators and RSS-to-email tools depend on them, and once they are cached they are cheap. Post comment feeds are the ones that grow with every article you publish, so they are the ones worth cutting.
After the changes, run the Step 1 commands again on a fresh day of logs. The post feed requests should now show up as 301 responses, and PHP-FPM should have room to breathe.
TL;DR: Count feed requests in the access log, make sure your cache serves feeds, remove the post comments feed link with feed_links_extra_show_post_comments_feed, and 301 /post-slug/feed/ back to the post in .htaccess or nginx so PHP never runs for those requests. Then block the few scrapers that are left.

