Full transparency: Links marked with (*) are affiliate links. Yes, we might earn a commission if you buy. No, that doesn’t mean we’re shilling garbage. We recommend what we’d actually use ourselves. Read our full policy.

The Problem Everyone Googles Wrong

You’re running WordPress behind Cloudflare with Fail2Ban for security. You fix NGINX to log real visitor IPs instead of Cloudflare’s proxy IPs. Everything works great. Then suddenly, legitimate users (including you) randomly get “not authorized” errors. Refresh the page, it works. Wait five minutes, happens again.

You check the logs. Fail2Ban is banning your users for “Untrusted X-Forwarded-For header” violations.

Most tutorials will tell you to disable Fail2Ban’s WordPress filter or add whitelists. That’s treating symptoms. Here’s what’s actually broken—and how to fix it properly.

The Real Problem: Three Security Systems Having a Conversation

  1. Cloudflare receives visitor traffic and proxies it to your server, adding headers:
    • CF-Connecting-IP: The real visitor IP (single, trusted value)
    • X-Forwarded-For: The proxy chain (can contain multiple IPs)
  2. NGINX is configured to trust Cloudflare’s IP ranges and extract the real IP from CF-Connecting-IP
  3. WordPress/PHP sees the X-Forwarded-For header and logs warnings about it being “untrusted” because it doesn’t match expected patterns
  4. Fail2Ban reads those warnings, sees repeated “Untrusted” messages, thinks it’s an attack pattern, bans the IP

Result: Your security system bans legitimate users because it’s confused about which proxy header to trust.

The Fix: Tell Fail2Ban What’s Actually an Attack

Don’t edit /etc/fail2ban/filter.d/wordpress-hard.conf directly—updates will overwrite it.

Create a local override:

sudo nano /etc/fail2ban/filter.d/wordpress-hard.local

Add this single line:

[Definition]
ignoreregex = Untrusted X-Forwarded-For

Restart Fail2Ban:

sudo systemctl restart fail2ban

Done. The “not authorized” errors stop.

But Wait—Aren’t We Creating a Security Hole?

Short answer: No, if you did this right.

Long answer: The “Untrusted X-Forwarded-For” warnings are noise, not attacks. Here’s why:

  • You’re using real_ip_header CF-Connecting-IP in NGINX (not X-Forwarded-For)
  • Cloudflare sets CF-Connecting-IP reliably for every request
  • The X-Forwarded-For header exists alongside it but isn’t used for IP detection
  • WordPress/PHP logs warnings about seeing it, but it doesn’t matter

The actual security hole would be direct access to your server bypassing Cloudflare, where attackers could inject fake headers. But you’ve already handled that (right?) by restricting your firewall to only accept traffic from Cloudflare’s IP ranges.

If you haven’t done that yet:

# Verify only Cloudflare IPs can reach ports 80/443
sudo ufw status
# or
sudo iptables -L -n

No Cloudflare IP restrictions? Fix that first. The Fail2Ban workaround won’t matter if attackers can bypass Cloudflare entirely.

The Complete NGINX Configuration (For Reference)

Your NGINX config should explicitly trust Cloudflare’s IP ranges:

# Cloudflare IP ranges - update periodically from cloudflare.com/ips/
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;

real_ip_header CF-Connecting-IP;
real_ip_recursive on;

Add IPv6 ranges if you’re using them. Test with sudo nginx -t before reloading.

What We Actually Fixed

Most people treat this as “Fail2Ban is too aggressive” and start whitelisting IPs or disabling filters. That’s lazy.

The real issue: three security systems were looking at different signals and reaching different conclusions about what’s legitimate traffic. We didn’t disable security—we clarified which signal to trust.

  • NGINX trusts: CF-Connecting-IP from Cloudflare IP ranges
  • Fail2Ban ignores: PHP warnings about other proxy headers
  • Firewall enforces: Only Cloudflare can reach the server

Security through clarity, not through hope.


LittleBig.Co — We don’t build what you ask for. We fix what’s actually broken.