GeoIP restrictions in Apache can fail when a reverse proxy trusts client-supplied forwarding headers. An attacker who controls X-Forwarded-For may be able to change the address a GeoIP module evaluates, bypassing a country-based block without changing the true TCP peer.
Understand the trust boundary
Requests from the public internet must not be allowed to declare an arbitrary “real client IP.” The forwarding header is meaningful only if it was set or sanitized by a trusted upstream proxy. Using GeoIPScanProxyHeaders On with untrusted headers can lead to incorrect country detection.
Test the current behavior
curl -sS -o /dev/null -w '%{http_code}\n' https://example.org/protected/
curl -sS -o /dev/null -w '%{http_code}\n' \
-H 'X-Forwarded-For: 192.0.2.10' \
https://example.org/protected/
The addresses are reserved for documentation. If the second request changes authorization outcomes even though the real source has not changed, your GeoIP decision may trust attacker-controlled data. Record relevant reverse-proxy and web-server log fields during the test.
Harden forwarding
For direct-to-internet Apache installations using the legacy GeoIP module, consider GeoIPScanProxyHeaders Off and base the country lookup on the actual peer. If a trusted load balancer sits in front, configure the real-IP module to accept forwarding headers only from explicit trusted addresses. Ensure the balancer overwrites inbound forwarding headers rather than appending untrusted client claims.
# Example for a direct-facing Apache vhost using mod_geoip:
GeoIPScanProxyHeaders Off
# Validate before applying during a maintenance window:
apachectl configtest
Don’t forget the backend
The same trust boundary affects rate limits, bot detection and log attribution. Whitelisting a country is a convenience, not an authentication mechanism. Favor explicit IP allowlists for management endpoints and restrict backend addresses so public clients cannot bypass the front proxy.