Skip to main content

mod_proxy_http Configuration — Modern Apache+Tomcat Integration

mod_proxy_http communicates with Tomcat using standard HTTP/1.1 and is the recommended method for new Apache+Tomcat integrations. It has no AJP vulnerabilities, configuration is straightforward, and you can leverage all standard Apache features including SSL termination, header control, and load balancing.


Enabling Modules​

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod headers
sudo a2enmod rewrite
sudo systemctl reload apache2

apache2ctl -M | grep "proxy_http"
# proxy_http_module (shared)

Basic Configuration​

# /etc/apache2/sites-available/myapp.conf

<VirtualHost *:80>
ServerName example.com

ProxyRequests Off # Disable forward proxy
ProxyPreserveHost On # Preserve original Host header

ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

Full Production Configuration​

# /etc/apache2/sites-available/myapp-ssl.conf

<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com

# HTTPS redirect
RewriteEngine On
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com

# SSL configuration
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

# Forward real client IP
RequestHeader set X-Real-IP "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-For "%{X-Forwarded-For}e env=HTTP_X_FORWARDED_FOR"
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s" env=!HTTP_X_FORWARDED_FOR
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
RequestHeader set X-Forwarded-Host "%{HTTP_HOST}s"

# Security headers
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

# Proxy settings
ProxyRequests Off
ProxyPreserveHost On

# Exclude static files
ProxyPass /static !
ProxyPass /favicon.ico !
ProxyPass /robots.txt !

# Dynamic requests → Tomcat
ProxyPass / http://127.0.0.1:8080/ connectiontimeout=10 timeout=60
ProxyPassReverse / http://127.0.0.1:8080/

# Static files served directly
Alias /static /var/www/myapp/static
<Directory "/var/www/myapp/static">
Options -Indexes
AllowOverride None
Require all granted
</Directory>

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
</IfModule>

# Logs
CustomLog ${APACHE_LOG_DIR}/myapp_access.log combined
ErrorLog ${APACHE_LOG_DIR}/myapp_error.log
</VirtualHost>

ProxyPass Path Handling​

Understanding the path transformation rules of ProxyPass is important.

# Pass path as-is
ProxyPass /app/ http://127.0.0.1:8080/app/
# /app/users → http://127.0.0.1:8080/app/users

# Transform path (watch trailing slashes)
ProxyPass /app/ http://127.0.0.1:8080/
# /app/users → http://127.0.0.1:8080/users

# API path branching
ProxyPass /api/v1/ http://127.0.0.1:8080/api/
# /api/v1/users → http://127.0.0.1:8080/api/users

ProxyPass ordering: Write more specific paths first.

# Correct order (specific paths first)
ProxyPass /api/admin http://127.0.0.1:8081/admin
ProxyPass /api/ http://127.0.0.1:8080/api/
ProxyPass / http://127.0.0.1:8080/

# Wrong order (/ catches all requests)
ProxyPass / http://127.0.0.1:8080/ # Do not place first
ProxyPass /api/ http://127.0.0.1:8080/api/ # Unreachable

Used when Tomcat sets a cookie path of /myapp/ that needs to be converted to /.

# When Tomcat sets Set-Cookie: Path=/myapp/
# Client accesses via Apache at /, causing a mismatch

ProxyPassReverseCookiePath /myapp/ /
ProxyPassReverseCookieDomain 127.0.0.1 example.com

Timeout and Retry Configuration​

# Global proxy timeout
ProxyTimeout 60

# Per-ProxyPass attributes
ProxyPass / http://127.0.0.1:8080/ \
connectiontimeout=10 \
timeout=60 \
retry=5 \
acquire=3000 \
max=100 \
ttl=300

# Keepalive connection reuse
SetEnv proxy-nokeepalive 0 # Enable keepalive
SetEnv proxy-initial-not-pooled 0 # Use connection pool
AttributeDescription
connectiontimeoutConnection establishment wait time (seconds)
timeoutResponse wait time (seconds)
retryRetry interval after error (seconds), 0 = immediate
acquireConnection pool wait time (ms)
maxMaximum number of connections
ttlMaximum connection reuse time (seconds)

Load Balancing​

sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
sudo a2enmod lbmethod_bybusyness
<Proxy "balancer://tomcatcluster">
# Add members
BalancerMember "http://127.0.0.1:8080" route=tomcat1 loadfactor=1
BalancerMember "http://192.168.1.11:8080" route=tomcat2 loadfactor=1

# Backup server (when all main servers are down)
BalancerMember "http://192.168.1.12:8080" status=+H

# Load balancing settings
ProxySet lbmethod=bybusyness # bybusyness: prefer least busy server
ProxySet stickysession=JSESSIONID # Sticky sessions
ProxySet nofailover=Off # Allow failover to other servers
</Proxy>

ProxyPass / balancer://tomcatcluster/
ProxyPassReverse / balancer://tomcatcluster/

# Balancer management page
<Location /balancer-manager>
SetHandler balancer-manager
Require ip 127.0.0.1 10.0.0.0/8
</Location>

Health Check Configuration​

# Apache 2.4.55+ built-in health check
<Proxy "balancer://tomcatcluster">
BalancerMember "http://127.0.0.1:8080" hcmethod=GET hcuri=/actuator/health hcinterval=10
BalancerMember "http://192.168.1.11:8080" hcmethod=GET hcuri=/actuator/health hcinterval=10
ProxySet lbmethod=byrequests
</Proxy>

Summary​

ItemConfiguration
Required modulesproxy + proxy_http
Basic directivesProxyPass + ProxyPassReverse
Real IP forwardingRequestHeader set X-Forwarded-For/Proto
Exclude static filesProxyPass /static !
Load balancingproxy_balancer + stickysession=JSESSIONID
Cookie pathProxyPassReverseCookiePath
TimeoutProxyTimeout + connectiontimeout=10 timeout=60