Skip to main content
Advertisement

mod_proxy_ajp Configuration

mod_proxy_ajp applies Apache's built-in proxy module to the AJP protocol. Configuration is simpler than mod_jk and uses Apache standard modules, but because it uses AJP, configuring Ghostcat vulnerability mitigations is mandatory.


Enabling the Module

sudo a2enmod proxy
sudo a2enmod proxy_ajp
sudo systemctl reload apache2

# Verify
apache2ctl -M | grep "proxy_ajp"
# proxy_ajp_module (shared)

Basic Configuration

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

<VirtualHost *:80>
ServerName example.com

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

# AJP proxy
ProxyPass / ajp://127.0.0.1:8009/
ProxyPassReverse / ajp://127.0.0.1:8009/
</VirtualHost>

Ghostcat Security Configuration (Required)

Tomcat server.xml

<Connector protocol="AJP/1.3"
address="127.0.0.1" <!-- Listen locally only -->
port="8009"
redirectPort="8443"
secretRequired="true" <!-- Secret required -->
secret="ApacheAjpSecret2024!" <!-- Strong secret -->
maxThreads="200"
connectionTimeout="20000"/>

Include Secret in Apache Configuration

<VirtualHost *:80>
ServerName example.com

ProxyRequests Off
ProxyPreserveHost On

# AJP connection with secret
<Proxy "ajp://127.0.0.1:8009?secret=ApacheAjpSecret2024!">
Require all granted
</Proxy>

ProxyPass / "ajp://127.0.0.1:8009/?secret=ApacheAjpSecret2024!"
ProxyPassReverse / ajp://127.0.0.1:8009/
</VirtualHost>

Forwarding Real Client IP (mod_remoteip)

sudo a2enmod remoteip
<VirtualHost *:80>
ServerName example.com

# Set actual client IP as REMOTE_ADDR
RemoteIPHeader X-Forwarded-For

ProxyRequests Off
ProxyPreserveHost On

# Real IP forwarding headers
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
RequestHeader set X-Forwarded-Proto "http"

ProxyPass / "ajp://127.0.0.1:8009/?secret=MySecret"
ProxyPassReverse / ajp://127.0.0.1:8009/
</VirtualHost>

SSL + mod_proxy_ajp

<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
ServerName example.com

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

# Notify Tomcat that this is HTTPS (request.isSecure() = true)
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"

ProxyRequests Off
ProxyPreserveHost On

<Proxy "ajp://127.0.0.1:8009?secret=ApacheAjpSecret2024!">
Require all granted
</Proxy>

ProxyPass / "ajp://127.0.0.1:8009/?secret=ApacheAjpSecret2024!"
ProxyPassReverse / ajp://127.0.0.1:8009/
</VirtualHost>

Static File Separation

<VirtualHost *:443>
ServerName example.com

# Static files — served directly by Apache
Alias /static /var/www/myapp/static
<Directory "/var/www/myapp/static">
Options -Indexes
AllowOverride None
Require all granted
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/png "access plus 1 month"
</Directory>

# Static paths are not passed to AJP
ProxyPass /static !
ProxyPass /favicon.ico !
ProxyPass /robots.txt !

# Dynamic requests → Tomcat
ProxyPass / "ajp://127.0.0.1:8009/?secret=MySecret"
ProxyPassReverse / ajp://127.0.0.1:8009/
</VirtualHost>

Load Balancing (mod_proxy_balancer)

sudo a2enmod proxy_balancer
sudo a2enmod lbmethod_byrequests
sudo a2enmod headers
<VirtualHost *:443>
ServerName example.com

# Define load balancer
<Proxy "balancer://tomcatcluster">
BalancerMember "ajp://127.0.0.1:8009?secret=MySecret" route=tomcat1
BalancerMember "ajp://192.168.1.11:8009?secret=MySecret" route=tomcat2

ProxySet lbmethod=byrequests
ProxySet stickysession=JSESSIONID
</Proxy>

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

# Balancer status page (internal only)
<Location /balancer-manager>
SetHandler balancer-manager
Require ip 127.0.0.1
</Location>
</VirtualHost>

Timeout Tuning

<VirtualHost *:443>
# AJP connection timeout
ProxyTimeout 60

# Per-path timeout (ProxyPass attributes)
ProxyPass / "ajp://127.0.0.1:8009/?secret=MySecret" \
timeout=60 \
connectiontimeout=10 \
retry=1 \
acquire=3000
</VirtualHost>
AttributeDescriptionDefault
timeoutAJP response wait time (seconds)Apache ProxyTimeout
connectiontimeoutConnection establishment wait (seconds)timeout value
retryWait time before retry after failure (seconds)60
acquireMax wait for connection pool acquisition (ms)Unlimited

Diagnostics

# Check AJP connection
ss -tlnp | grep 8009

# Apache error log
tail -f /var/log/apache2/error.log

# Example mod_proxy_ajp errors
# [error] ajp_read_header: ajp_ilink_receive failed
# → Restart Tomcat or check AJP connection pool exhaustion

# Validate configuration
sudo apache2ctl configtest

Summary

ItemConfiguration
Required modulesproxy + proxy_ajp
Ghostcat mitigationTomcat: secretRequired=true + Apache: ?secret=...
Listen restrictionTomcat: address="127.0.0.1"
SSLRequestHeader set X-Forwarded-Proto "https"
Load balancingproxy_balancer + stickysession=JSESSIONID
Recommended migrationUse mod_proxy_http for new systems
Advertisement