Panellicense

Run Imunify360 behind Cloudflare without losing visitor IPs

Cloudflare proxying makes every request look like it came from a Cloudflare edge IP, breaking Imunify360's brute-force and WAF detection. Restore real visitor IPs and wire up the Cloudflare integration the right way.

8 min readUpdated 2026-05-16imunify360 · cloudflare · cf-connecting-ip · mod_remoteip
schema: HowToschema: FAQPageschema: BreadcrumbList

Put Cloudflare in front of a cPanel server and Imunify360 starts behaving badly. Brute-force blocks fire against 172.69.x.x, the WAF grey-list fills with Cloudflare edge ranges, and within a day a single proxy IP is banned — which silently locks out every visitor from that Cloudflare PoP. The cause is the obvious one: Apache and Imunify see the Cloudflare edge as the client, because that's literally what the TCP connection looks like.

The fix is two layers. First, restore the real client IP at the web-server layer so logs and ModSecurity see the actual visitor. Second, enable Imunify360's native Cloudflare integration so blocks are pushed upstream to Cloudflare's firewall rather than landing on the wrong IP at your edge.

Prerequisites

  • Cloudflare in front of a cPanel server with Imunify360 6.x or later installed. If you haven't installed it yet, see installing Imunify360 on cPanel.
  • Apache with EasyApache 4 or LiteSpeed Enterprise as the web server. The exact module differs between the two.
  • A Cloudflare API token with Zone.Firewall Services:Edit permission on the zones you want Imunify to manage. Global API keys also work but are over-permissioned — use a scoped token.
  • Root SSH access.

Step 1 — Restore real visitor IPs at the web server

Cloudflare passes the real client IP in the CF-Connecting-IP header (and X-Forwarded-For as a fallback). Apache and LiteSpeed both need a module to read that header and treat the value as the connection IP for the rest of the request lifecycle — logs, mod_security, PHP's $_SERVER['REMOTE_ADDR'], everything.

Apache (EasyApache 4)

EasyApache ships mod_remoteip by default. Verify it's loaded:

httpd -M | grep remoteip

You should see remoteip_module (shared). If not, install it:

yum install ea-apache24-mod_remoteip

Drop the configuration into a global include so it survives EasyApache rebuilds:

nano /etc/apache2/conf.d/includes/pre_main_global.conf

Add:

RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxyList /etc/apache2/conf.d/cloudflare-ips.txt

Now populate the trusted-proxy list with Cloudflare's current edge ranges:

{ curl -s https://www.cloudflare.com/ips-v4; echo; curl -s https://www.cloudflare.com/ips-v6; } > /etc/apache2/conf.d/cloudflare-ips.txt

Restart Apache:

/scripts/restartsrv_httpd

Cloudflare updates its IP ranges occasionally — schedule a weekly cron to refresh cloudflare-ips.txt and reload Apache. A short script in /etc/cron.weekly/refresh-cloudflare-ips:

#!/bin/bash
TMP=$(mktemp)
{ curl -fsS https://www.cloudflare.com/ips-v4; echo; curl -fsS https://www.cloudflare.com/ips-v6; } > "$TMP" \
  && mv "$TMP" /etc/apache2/conf.d/cloudflare-ips.txt \
  && /scripts/restartsrv_httpd

Make it executable: chmod +x /etc/cron.weekly/refresh-cloudflare-ips.

LiteSpeed Enterprise

LiteSpeed has built-in support — no module install needed. In WebAdmin, go to Configuration → Server → General → Use Client IP in Header and set to Trusted IP Only. Then under Configuration → Server → IP Access Control → Allowed List, add the Cloudflare ranges from https://www.cloudflare.com/ips-v4 and ips-v6 with a trailing T to mark them trusted, e.g. 173.245.48.0/20T.

Graceful restart:

/usr/local/lsws/bin/lswsctrl restart

Verify real IPs are coming through

Tail the access log and load a page from your phone (or any non-server IP):

tail -f /usr/local/apache/domlogs/example.com

The first column should be the real client IP, not 172.x or 108.x. If you still see Cloudflare ranges, the RemoteIPHeader directive didn't take effect — check httpd -t -D DUMP_INCLUDES to confirm the include is being read.

Step 2 — Enable Imunify360's Cloudflare integration

Now that the web server sees real IPs, Imunify will already start blocking the right addresses at your server. But those blocks happen after the request reaches your edge — wasted bandwidth and CPU. The Cloudflare integration pushes the same blocks one hop upstream so attackers get rejected by Cloudflare's WAF before your server ever sees the packet.

Open Imunify360 in WHM → Plugins → Imunify360 → Settings → Cloudflare. Add your account:

  • Email: the Cloudflare account email
  • API token: the scoped token from the prerequisites (recommended) or the Global API Key
  • Zones: leave blank to manage all zones, or specify a comma-separated list

Save. Imunify validates the credentials and lists managed zones.

To do the same from the CLI:

imunify360-agent cloudflare add \
  --email admin@example.com \
  --api-key 'YOUR_SCOPED_TOKEN'

Once added, every IP that Imunify decides to block (brute force, WAF event, RBL hit) gets a corresponding firewall rule pushed to the matching Cloudflare zone via the API. Rules are tagged imunify360 in the Cloudflare dashboard so they're easy to audit and revoke.

Step 3 — Lock the origin to Cloudflare-only

With visitor IPs restored and blocks pushed upstream, the last step closes the obvious bypass: an attacker who finds the origin IP can hit it directly and skip Cloudflare entirely. Tighten the origin firewall to accept HTTP/HTTPS only from Cloudflare ranges.

The easiest way is via CSF — install it if you don't already have it, and add to /etc/csf/csfpre.sh:

#!/bin/bash
# Allow HTTP/HTTPS only from Cloudflare
for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
  iptables -I INPUT -p tcp -m multiport --dports 80,443 -s "$ip" -j ACCEPT
  ip6tables -I INPUT -p tcp -m multiport --dports 80,443 -s "$ip" -j ACCEPT 2>/dev/null
done
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP
ip6tables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP 2>/dev/null

Make executable and reload CSF:

chmod +x /etc/csf/csfpre.sh
csf -r

If you're using Imunify360 alongside CSF, read running Imunify360 alongside CSF without double-blocking before locking down — the two need to be coordinated so CSF doesn't pre-empt Imunify's block decisions.

Common failure modes

Apache logs still show Cloudflare IPs in the first column. The RemoteIPHeader directive didn't load. Check httpd -t for syntax errors, confirm the include path matches your distro (/etc/apache2/conf.d/includes/ on EasyApache 4, /etc/httpd/conf.d/ on stock), and verify with httpd -M | grep remoteip.

Imunify blocks customers in entire regions after enabling Cloudflare integration. You enabled the integration before step 1. Imunify pushed Cloudflare edge IPs to Cloudflare's firewall. Disable the integration in Imunify, log into Cloudflare → Security → WAF → Custom rules, delete all rules tagged imunify360, fix the real-IP restoration, then re-enable.

CF-Connecting-IP header is missing. The request didn't come through Cloudflare. Either the DNS record is greyed (DNS-only, no proxy) or someone hit the origin IP directly. Step 3 closes the second case.

Cloudflare rule limit reached. Free and Pro plans cap WAF custom rules at 5 and 20 respectively. Imunify can easily exceed this on a busy server. Either upgrade the Cloudflare plan or set the integration to only push high-severity blocks — imunify360-agent config update '{"CLOUDFLARE": {"min_severity": "high"}}'.

Does Imunify360's Cloudflare integration work with free Cloudflare plans?+
Yes. The API endpoints Imunify uses (WAF custom rules) are available on free plans. The cap is much lower — 5 custom rules vs 1000 on Enterprise — so on a busy server you may need to upgrade or filter which blocks get pushed upstream.
Will mod_remoteip break sites that already have their own IP-handling code?+
Rarely. PHP's $_SERVER['REMOTE_ADDR'] returns the rewritten real IP automatically. Sites reading X-Forwarded-For directly (older WordPress plugins, custom code) will still get the unmodified header. Most plugins handle both — test login flows after enabling.
Can I use a Cloudflare scoped token instead of the Global API key?+
Yes, and you should. Create a token at dash.cloudflare.com → My Profile → API Tokens with permissions Zone.Firewall Services:Edit and Zone.Zone:Read, scoped to the zones you want Imunify to manage. Imunify accepts both authentication methods.
What happens to existing Imunify blocks when I enable Cloudflare integration?+
Existing local grey-list entries are not retroactively pushed to Cloudflare. Only new block events after the integration is enabled get propagated. To force a full sync, restart imunify360 with imunify360-agent restart — but this can spike API calls if your grey-list has thousands of entries.
Does Cloudflare's Bot Fight Mode conflict with Imunify360?+
No, they operate at different layers. Cloudflare's bot mode challenges suspicious requests before they reach your server. Imunify acts on what does reach the server. They complement each other — but be aware that Bot Fight Mode challenges can create what look like high false-positive rates in Imunify if challenged users retry and trigger your rate limits.
How often should I refresh the Cloudflare IP list?+
Cloudflare changes ranges a few times a year, usually adding rather than removing. Weekly refresh via cron is more than enough. If a customer reports their visitors are being marked as untrusted (logs show edge IPs again), force-refresh manually and reload Apache.

Next steps

Switch in an afternoon

Switch from your current reseller — free.

We migrate active cPanel, Plesk, LiteSpeed and CloudLinux licenses from any reseller. We prorate the first month so you never pay twice, and your customers see zero downtime during the swap.