If your WordPress website is proxied behind Cloudflare, the IP address reported to your server isn’t the actual IP address of the original request. It’s a Cloudflare IP. Most of the time, this doesn’t matter, but if you’re using WP fail2ban, this causes issues.
By default, fail2ban in combination with WP fail2ban works by using
iptables to block IP addresses that are trying (and failing) to log into your WordPress website.
When Cloudflare proxies your website, the incoming request is going to show a Cloudflare IP address. That’s the whole point of a proxy. That means that fail2ban is going to block that Cloudflare IP address.
But every request to your site is coming from Cloudflare IP addresses. Which means this will block a bunch of good traffic in addition to the bad. Oops.
What we need to do is tell WP fail2ban to log the original IP address, and then ensure that our web server is blocking requests based on the original IP address.
Configure WP fail2ban
First, we need to tell WP fail2ban what to log. Fortunately, the developer has already considered this. You can configure known IP addresses for WP fail2ban to handle by defining a
WP_FAIL2BAN_PROXIES constant in your
wp-config.php file. When this constant is configured, WP fail2ban will log the
X-Forwarded-For request header instead of the reported remote IP. Here’s an example using Cloudflare’s IP addresses as of this writing:
Though infrequent, those IP addresses can change over time. For an automated solution that leverages Cloudflare’s API reporting of its own IP addresses, see this plugin we developed. Otherwise, Cloudflare reports any IP changes via various email and social media channels, so make sure you’re in the loop so you can respond appropriately.
Restore Original IP Addresses
If you only do this first step, you run into a situation where the correct IP addresses are banned, but the server still isn’t reading the correct IP address from the incoming requests, so it won’t actually block any traffic. So that’s pretty worthless.
To fix this, you also need to make sure your server is reading the correct IP address off of incoming requests.
I won’t rehash Cloudflare’s documentation on all of this, which includes documentation for Apache and other web servers. But I will quickly show how to get this working with Nginx.
Check for the Correct Module
First, your version of Nginx has to have been compiled with
ngx_http_realip_module. While it isn’t technically a default module, services like Laravel Forge do include it. Run the following to get a formatted version of your Nginx configuration, including installed modules, and ensure it’s included (this tip found here):
nginx -V 2>&1 | tr ' ' '\n'
nginx version: nginx/1.17.3 built with OpenSSL 1.1.1 ... --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module ...
Create a Configuration
Next, we need to put Cloudflare’s IPs into a configuration for this module. I elected to create a dedicated
conf file (on Forge, in a file at
/etc/nginx/snippets/cloudflare_ips.conf). Cloudflare gives us the correct configuration with their IP addresses:
Code language: PHP (php)
set_real_ip_from 126.96.36.199/22; set_real_ip_from 188.8.131.52/22; set_real_ip_from 184.108.40.206/22; set_real_ip_from 220.127.116.11/13; set_real_ip_from 18.104.22.168/14; set_real_ip_from 22.214.171.124/18; set_real_ip_from 126.96.36.199/22; set_real_ip_from 188.8.131.52/18; set_real_ip_from 184.108.40.206/15; set_real_ip_from 220.127.116.11/13; set_real_ip_from 18.104.22.168/20; set_real_ip_from 22.214.171.124/20; set_real_ip_from 126.96.36.199/20; set_real_ip_from 188.8.131.52/22; set_real_ip_from 184.108.40.206/17; set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2c0f:f248::/32; set_real_ip_from 2a06:98c0::/29; #use any of the following two real_ip_header CF-Connecting-IP; #real_ip_header X-Forwarded-For;
Once again, this list will need to be updated when Cloudflare updates their IP addresses from time to time. Unfortunately, I don’t have a nifty, automated solution for that.
Reference Your New Config
Assuming, like me, you didn’t slap this directly into your main Nginx configuration, you want to reference this new configuration from your main Nginx conf file, within the
server block. Once again, if you use Laravel Forge, this configuration will look familiar:
Code language: PHP (php)
# FORGE CONFIG (DO NOT REMOVE!) include forge-conf/example.com/server/*; # Restore remote IPs include snippets/cloudflare_ips.conf;
And you’re good to go!