When Cloudflare is used as a protection layer in front of SKUDONET, traffic behavior changes significantly. The load balancer no longer sees the client’s real IP address and instead receives the IP of Cloudflare nodes.
This has a direct impact on traditional network-based security mechanisms (L3/L4), such as IP blacklists.
Problem
In a typical scenario, traffic flows through the following chain:
Client → Cloudflare → SKUDONET → Backend
The REMOTEE_ADDR field no longer contains the client’s IP, but Cloudflare’s.
Consequences
- L3/L4 blacklists become ineffective
- Cloudflare nodes may be blocked instead of the actual attacker
- The ability to filter malicious traffic by source IP is lost
Cloudflare partially addresses this issue by adding the real client IP in an HTTP header:
CF-Connecting-IP: <real client IP>
Therefore, filtering must move from the network layer (L3/L4) to the application layer (L7).
Solution
The solution is to move blacklist logic to the WAF (Web Application Firewall), where HTTP headers can be analyzed.
Approach
- Read the CF-Connecting-IP header
- Compare its value against a blacklist
- Block the request if there is a match
- Cache the result to improve performance
Note: Caching is critical, it avoids repeated disk access and reduces CPU usage significantly on high-traffic deployments.
Rule configuration
Below is the recommended configuration based on ModSecurity:
SecAction \
"id:99295000,phase:1,pass,nolog,initcol:ip_cloudflare=%{REQUEST_HEADERS.CF-Connecting-IP}"
SecRule IP:is_bad "@eq 1" \
"id:99295001,\
phase:1,\
deny,\
status:403,\
log,\
t:none,\
msg:'SKD Global: Blocked cached bad IP from CF-Connecting-IP'"
SecRule REQUEST_HEADERS:CF-Connecting-IP "@ipMatchFromFile /usr/local/skudonet/config/ipds/blacklists/lists/CIArmy.txt" \
"id:99295002,phase:1,deny,status:403,log,t:none,msg:'SKD Global: Blocked blacklisted IP from CF-Connecting-IP',setvar:ip_cloudflare.is_bad=1,expirevar:ip_cloudflare.is_bad=3600"
Where to configure the rule
Navigate to:
Intrusion Prevention & Detection System → WAF → Files
- Create a new file
- Select type: Ruleset
- Add the configuration above
How the rule works
1. IP initialization
SecAction ...
- Creates a persistent collection (ip_cloudflare)
- Uses the CF-Connecting-IP as the key
- Allows storing information per client
2. Cache-based blocking
SecRule IP:is_bad "@eq 1"
- Checks whether the IP has already been marked as malicious
- If so, it blocks immediately (HTTP 403)
Advantages
- Avoids repeated file lookups
- Reduces CPU and I/O usage
- Improves response time
3. Blacklist comparison
SecRule REQUEST_HEADERS:CF-Connecting-IP "@ipMatchFromFile ..."
- Reads the real client IP from the header
- Compares it against the blacklist file
If there is a match:
- The request is blocked
- The IP is marked as malicious
- It is cached for 1 hour
Variant: domain-based filtering (Host)
In multi-service environments, it may be useful to apply the blacklist only to specific domains.
Example
SecRule REQUEST_HEADERS:Host "@rx (?i)^portal\.domain\.com\.tr$"
Full example
SecAction \
"id:99295010,phase:1,pass,nolog,initcol:ip_cloudflare=%{REQUEST_HEADERS.CF-Connecting-IP}"
SecRule REQUEST_HEADERS:Host "@rx (?i)^portal\.domain\.com\.tr$" \
"id:99295011,phase:1,deny,status:403,log,t:none,msg:'SKD Global: Blocked cached bad IP for target host',chain"
SecRule IP:is_bad "@eq 1" \
"t:none"
SecRule REQUEST_HEADERS:CF-Connecting-IP "@ipMatchFromFile /usr/local/skudonet/config/ipds/blacklists/lists/CIArmy.txt" \
"id:99295012,phase:1,deny,status:403,log,t:none,msg:'SKD Global: Blocked blacklisted IP from CF-Connecting-IP for target host',chain"
SecRule REQUEST_HEADERS:Host "@rx (?i)^portal\.domain\.com\.tr$" \
"t:none,setvar:ip_cloudflare.is_bad=1,expirevar:ip_cloudflare.is_bad=3600"
This variant enables:
- Apply rules only to a specific service
- Avoid impacting other domains
- Greater control in multi-tenant or reverse proxy environments
Best practices
When using Cloudflare in front of SKUDONET:
- Allow only Cloudflare IPs at the network level
- Use CF-Connecting-IP as the real client source
- Apply blacklist filtering in the WAF (L7)
- Implement caching to optimize performance
Advantages of this approach
- Maintains protection against malicious IPs
- Avoids blocking Cloudflare infrastructure
- Compatible with existing blacklists
- Scales well even with large lists
- Reduces CPU usage thanks to in-memory caching

