Since these are configured in Discourse, I’m pretty sure that the IPs are blocked by Discourse, not NGINX, so they’ll show up in the NGINX logs. It’s Discourse that’s blocking them, not nginx. If you want to block them so that they don’t show up in the NGINX logs you could block them with your firewall.
Thanks Jay, you’re right about how it would still show up in the Nginx logs if blocked at the application level. However, the blocking at the application level isn’t doing what I would expect either. I tried connecting via a VPN and then I added my own IP address to the “Screened IPs” list, but it still allowed me to navigate the forum. I think that it must be called “Screened IPs” and not “Blocked IPs” because maybe it only prevents those IPs from registering an account?
What I need is for the Discourse app to deny access to the requested pages for requests coming from those addresses, and especially for it to not render those pages, as all of those requests are pegging the CPU.
Does Discourse see that you’re coming from one of the banned IPs if you go look at the user record from /admin/users? (If you’re behind a proxy like cloudflare, then Discourse may be seeing your proxy IP and not the IP of the user.)
Yep, when I connect to the VPN and then look at the last IP of my user account in Discourse it shows the IP that my VPN gave me. However, when I open an incognito browser window and try to register a new account then it doesn’t allow it:
New registrations are not allowed from your IP address.
So it appears that “Screened IPs” are just for registration, not for completely disallowing access to the website.
So what would be the easiest method to deny requests from an IP or range of IPs? I found this, but it appears to be the opposite of what I need (whitelist rather than a blacklist), and messing around with the Nginx config inside the container feels like a total hack job:
I think doing it with UFW or IPTABLES . That removes stops it before Discourse gets involved. I’m always vague terrified mucking with firewalls for fear that I’ll lock myself out, but if you target just port 443 you aren’t in any danger.
Exactly my fear as well. But I did enable it on the host and yet it’s still not blocking the problematic IP range. Apparently it needs a convoluted set of rules to make it apply the deny rules to the Docker containers.
Oh. Yeah. That’s totally true. I ended up blocking access from my docker-based web servers to the docker-based postgres database (likely not one of your problems).
/var/discourse/launcher enter app
apt install nano
nano /etc/nginx/conf.d/discourse.conf
And in the server { block add:
## 2025-10-27
deny 12.34.0.0/16;
Then save and
nginx -t
service nginx reload
I don’t quite understand why I’m still seeing hits from 12.34.x.x in the Nginx access.log, but it does seem to be working because the CPU usage is now normal again. Incredibly convoluted process IMHO, but good enough for now to get the site back up off its knees.
I think they’re both old aliases that are now mapped to systemd commands, so systemctl restart nginx would be the most proper. EDIT:It appears that systemctl doesn’t work inside Docker. Here are some explanations about the differences:
They seem to be mainly hitting permalinks now (migrated from an old forum platform). Is there some kind of a loophole with permalinks that lets it get around the deny rule? I haven’t checked the production.log yet.
Overall I would say that the lack of a GUI to inspect the access logs and no app-level IP blocklist is quite a significant limitation of Discourse. It’s an infrequent occurrence, but when you do get hit by one of these bot/scraper/crawler attacks you just want to immediately identify the source and mitigate it, without mucking about in a bunch of config files and arcane commands, especially not levels deep inside a Docker container with all the weird abstraction that takes place there. The very old forum platform I migrated from showed a simple list of the users or IPs with the highest number of requests during an adjustable time window, and it could even be filtered by which users and/or IPs occupied the highest amount of CPU time. That way I could quickly identify the offending address or range, and then there was a point-n-click interface to add it to a blocklist, and it would throw a 404 for requests from those IPs.
If you have a deny rule, things still get logged in nginx. And it works correctly, because there is a 403 there, which means the client was denied access.
These requests are not being forwarded to Discourse. If you want to know whether your blocking is working correctly, you should either
look in the Discourse production.log instead
look in the nginx logs but ignore any entry that has a 403 in the HTTP response code field.