Skip to main content
Advertisement

HTTP Proxy Integration — Mastering proxy_pass

Using proxy_pass in Nginx to integrate with Tomcat is the most standard approach. This chapter covers everything you must know in production: header forwarding, real IP propagation, and upstream configuration.


Basic proxy_pass Configuration

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://127.0.0.1:8080;
}
}

This alone enables Nginx to forward requests to Tomcat. In production, you'll need additional settings for header forwarding, timeouts, and buffering.


Defining Backend with upstream Block

The upstream block makes it explicit and easy to scale to multiple instances.

# /etc/nginx/conf.d/upstream.conf
upstream tomcat_backend {
server 127.0.0.1:8080;
keepalive 32;
}
# /etc/nginx/sites-available/myapp.conf
server {
listen 443 ssl;
server_name example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

location / {
proxy_pass http://tomcat_backend;
}
}

Required Proxy Headers

# /etc/nginx/snippets/proxy-headers.conf
# Include this file for reuse

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;
proxy_set_header X-Forwarded-Port $server_port;

# Required for keepalive connections
proxy_http_version 1.1;
proxy_set_header Connection "";
HeaderValueRole in Tomcat
Host$hostVirtual host matching
X-Real-IP$remote_addrReal client IP
X-Forwarded-For$proxy_add_x_forwarded_forProxy chain IP list
X-Forwarded-Proto$schemeOriginal protocol (http/https)

Real IP Propagation (RemoteIpValve)

Configure Tomcat to return the real client IP from request.getRemoteAddr() instead of Nginx's IP (127.0.0.1).

Nginx Configuration

set_real_ip_from   127.0.0.1;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

Tomcat server.xml Configuration

<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto"
internalProxies="127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+"/>

With this:

  • request.getRemoteAddr() → returns actual client IP
  • request.isSecure() → returns true when Nginx received HTTPS
  • Logs record the real client IP

Complete Proxy Configuration Example

upstream tomcat_backend {
server 127.0.0.1:8080;
keepalive 32;
}

server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

client_max_body_size 50m;
client_body_buffer_size 128k;

location / {
proxy_pass http://tomcat_backend;

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;
proxy_set_header X-Forwarded-Port $server_port;

proxy_http_version 1.1;
proxy_set_header Connection "";

proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
}

error_page 502 503 504 /error/5xx.html;
location = /error/5xx.html {
root /var/www/myapp;
internal;
}

access_log /var/log/nginx/myapp_access.log combined;
error_log /var/log/nginx/myapp_error.log warn;
}

proxy_pass URI Handling Caveat

The behavior changes depending on whether you include a URI in proxy_pass.

# Without URI — forwards the full location path
location /app/ {
proxy_pass http://127.0.0.1:8080;
# /app/foo → http://127.0.0.1:8080/app/foo
}

# With URI — replaces the location path
location /app/ {
proxy_pass http://127.0.0.1:8080/;
# /app/foo → http://127.0.0.1:8080/foo
}

# API path transformation example
location /api/ {
proxy_pass http://127.0.0.1:8080/api/v1/;
# /api/users → http://127.0.0.1:8080/api/v1/users
}

Special Path Handling

WebSocket Proxy

location /ws/ {
proxy_pass http://tomcat_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}

SSE (Server-Sent Events) Proxy

location /events/ {
proxy_pass http://tomcat_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off; # Must disable buffering for SSE
proxy_cache off;
proxy_read_timeout 3600s;
}

File Upload Path

location /upload/ {
proxy_pass http://tomcat_backend;
client_max_body_size 500m;
proxy_request_buffering off;
proxy_read_timeout 600s;
}

Validating Proxy Configuration

# Validate Nginx config
sudo nginx -t

# Reload (zero-downtime)
sudo nginx -s reload

# Test proxy connection
curl -v https://example.com/api/test
# Check X-Forwarded-For, X-Forwarded-Proto headers

Summary

SettingDetails
Upstreamupstream block + keepalive 32
Required headersHost, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto
HTTP versionproxy_http_version 1.1 + Connection "" (required for keepalive)
Real IPNginx: real_ip_header + Tomcat: RemoteIpValve
WebSocketUpgrade: $http_upgrade + Connection: upgrade
SSEproxy_buffering off + proxy_cache off
Advertisement