When running Tomcat behind an Nginx reverse proxy, you'll notice that all access logs show 127.0.0.1 as the client IP. This happens because Tomcat sees Nginx as the client rather than the actual remote client. While Nginx properly sets the X-Real-IP header, Tomcat doesn't automatically use it for logging.
Tomcat provides a built-in solution through its Valve component. Here's how to configure it in server.xml:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-real-ip"
internalProxies="127\.0\.0\.1"
proxiesHeader="x-forwarded-for"
trustedProxies="127\.0\.0\.1" />
This configuration tells Tomcat:
- Use the X-Real-IP header for client IP (remoteIpHeader)
- Trust requests coming from 127.0.0.1 (internalProxies)
- Handle the X-Forwarded-For header properly (proxiesHeader)
Make sure your Nginx config includes these critical headers:
location / {
proxy_pass http://tomcat;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
After restarting both Nginx and Tomcat, your access logs should now show:
203.0.113.45 - - [10/Jun/2013:11:25:48 +0600] "GET /app/welcome HTTP/1.1" 200 10571
198.51.100.22 - - [10/Jun/2013:11:25:49 +0600] "GET /app/dashboard HTTP/1.1" 200 8732
For more complex setups with multiple proxies:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-real-ip"
internalProxies="192\.168\.1\.\d+|127\.0\.0\.1"
proxiesHeader="x-forwarded-for"
trustedProxies="192\.168\.1\.\d+|127\.0\.0\.1" />
This regex pattern allows for multiple trusted proxy IPs in your internal network.
The RemoteIpValve has minimal performance overhead. In benchmarks, the impact is typically less than 1% of total request processing time, making it suitable for production environments.
When running Tomcat 7 behind an Nginx reverse proxy, you'll notice access logs only record the proxy's IP (typically 127.0.0.1) rather than actual client IPs. This happens because Tomcat's default Valve implementation doesn't automatically respect the X-Real-IP
or X-Forwarded-For
headers.
set_real_ip_from 127.0.0.1;
...
location / {
proxy_pass http://tomcat;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Add the RemoteIpValve to your $CATALINA_HOME/conf/server.xml
inside the <Host>
element:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-real-ip"
internalProxies="127\.0\.0\.1"
trustedProxies=""
proxiesHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto" />
If you specifically want to modify just the logging behavior without affecting request handling:
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log"
suffix=".txt"
pattern="%{X-Real-IP}i %l %u %t "%r" %s %b" />
After restarting Tomcat, verify with curl:
curl -H "X-Real-IP: 203.0.113.42" http://yourserver
Your Tomcat access logs should now show 203.0.113.42 instead of 127.0.0.1.
For complex proxy chains (e.g., Cloudflare → Nginx → Tomcat), adjust internalProxies:
internalProxies="192\.168\.1\.1|127\.0\.0\.1|cloudflare_ip_range"
The RemoteIpValve does add some overhead. For high-traffic sites, consider:
- Compiling a custom AccessLogValve implementation
- Using regex optimizations in internalProxies
- Offloading logging to Nginx