Apache+Tomcat Integration Pro Tips
This chapter covers advanced know-how for diagnosing and resolving real-world problems in Apache+Tomcat integration environments. From leveraging the mod_jk status page to connection debugging, performance tuning, and incident response — everything you need for production operations.
Leveraging the mod_jk Status Page
The mod_jk jkstatus handler lets you monitor worker status and load balancing statistics in real time.
Configuration
# workers.properties — Add status worker
worker.list=worker1,jkstatus
worker.jkstatus.type=status
worker.jkstatus.read_only=true # Read-only is mandatory in production
# Apache VirtualHost — Status page path
<Location /jkstatus>
JkMount jkstatus
Require ip 127.0.0.1 10.0.0.0/8 # Internal network only
</Location>
Information Available on the Status Page
| Item | Description |
|---|---|
| State | OK / ERR (worker status) |
| Elected | Total requests processed |
| Errors | Error count |
| Sessions | Currently active sessions |
| Route | Sticky session routing key |
| LBF | Load balancing factor |
# Check status via CLI
curl -s "http://localhost/jkstatus/?mime=prop" | grep worker
# Specific worker status
curl -s "http://localhost/jkstatus/?cmd=show&w=worker1"
# Enable/disable worker (only when read_only=false)
curl -s "http://localhost/jkstatus/?cmd=update&w=worker1&ac=0" # Disable
curl -s "http://localhost/jkstatus/?cmd=update&w=worker1&ac=1" # Enable
Integration Debugging — JkLogLevel
Increase JkLogLevel to analyze detailed logs.
# mod_jk log levels (by severity)
JkLogLevel debug # Most verbose (do NOT use in production — performance impact)
JkLogLevel info
JkLogLevel warn # Default recommended
JkLogLevel error
# Real-time log monitoring
tail -f /var/log/apache2/mod_jk.log
# Filter for errors
grep -i "error\|fail\|timeout" /var/log/apache2/mod_jk.log | tail -50
# Logs related to specific worker
grep "worker1" /var/log/apache2/mod_jk.log | tail -30
Common mod_jk Errors
| Error Message | Cause | Resolution |
|---|---|---|
Timeout waiting for a reply | Tomcat response delay | Increase socket_timeout, check Tomcat load |
ajp_ilink_receive failed | AJP connection dropped | Restart Tomcat, check connection pool |
No worker found for name | workers.properties misconfiguration | Check worker.list and worker names |
All workers are in error state | All Tomcat instances down | Check Tomcat process and AJP port |
mod_proxy_http Debugging
# Temporarily increase Apache log level
LogLevel proxy:debug proxy_http:debug
# Extract only mod_proxy related errors
grep -i "proxy\|ajp\|backend" /var/log/apache2/error.log | tail -50
# Check proxy connection status
curl -v http://localhost/ 2>&1 | grep -E "< HTTP|Connected|Request"
# Test backend (Tomcat) response directly
curl -v http://127.0.0.1:8080/ 2>&1 | head -30
Common mod_proxy Errors
| HTTP Code | Error | Cause | Resolution |
|---|---|---|---|
| 502 Bad Gateway | No backend response | Tomcat down, port mismatch | Check Tomcat status, ss -tlnp | grep 8080 |
| 503 Service Unavailable | Connection pool exhausted | Request surge | Increase max, check Tomcat threads |
| 504 Gateway Timeout | Response timeout | Slow query/processing | Increase timeout, analyze slow queries |
| 500 Internal Server Error | Tomcat internal error | Application exception | Check Tomcat catalina.out |
Connection Pool Optimization
mod_jk workers.properties Tuning
# workers.properties — Connection pool optimization
worker.worker1.type=ajp13
worker.worker1.host=127.0.0.1
worker.worker1.port=8009
# Pool size = 75~80% of Tomcat maxThreads
worker.worker1.connection_pool_size=75
# Idle connection timeout: clean up long-unused connections
worker.worker1.connection_pool_timeout=600
# Socket timeout: max wait for Tomcat response
worker.worker1.socket_timeout=60
# TCP keepalive: enable if firewalls are dropping connections
worker.worker1.socket_keepalive=true
# Retry settings
worker.worker1.retries=2
worker.worker1.recovery_options=7
mod_proxy_http Connection Pool Tuning
# Global proxy connection settings
<IfModule mod_proxy.c>
# Limit total open connections
ProxyMaxForwards 10
</IfModule>
# Fine-grained control with ProxyPass attributes
ProxyPass / http://127.0.0.1:8080/ \
connectiontimeout=5 \ # Max 5 seconds to establish connection
timeout=60 \ # Max 60 seconds for response
retry=0 \ # Retry immediately on error (0=immediate)
acquire=3000 \ # Max 3 seconds waiting for connection pool
max=100 \ # Max 100 connections
ttl=300 # Max 300 seconds connection reuse
# Enable keepalive (maintain TCP connections to Tomcat)
SetEnv proxy-nokeepalive 0
SetEnv proxy-initial-not-pooled 0
Header Debugging
Verify that client IP and SSL information is correctly forwarded to Tomcat.
# Expose internal info in response headers for debugging (development only)
<VirtualHost *:443>
Header always set X-Debug-Remote-Addr "%{REMOTE_ADDR}s"
Header always set X-Debug-Forwarded-For "%{X-Forwarded-For}e"
Header always set X-Debug-Proto "%{X-Forwarded-Proto}e"
</VirtualHost>
# Check headers received by Tomcat (Spring Boot)
# application.properties
# logging.level.org.apache.tomcat=DEBUG
# Verify headers via curl
curl -I -H "X-Test: hello" https://example.com/
Sticky Session Troubleshooting
# Verify session created on Tomcat 1
curl -c cookies.txt https://example.com/login
cat cookies.txt
# JSESSIONID=abc123.tomcat1 ← .tomcat1 is the routing key
# Confirm next request routes to the same Tomcat
curl -b cookies.txt https://example.com/dashboard
Sticky Session Failure Checklist
1. Tomcat server.xml — Verify jvmRoute setting
<Engine jvmRoute="tomcat1">
2. workers.properties — Verify route setting
worker.tomcat1.route=tomcat1
3. Confirm JSESSIONID cookie contains .tomcat1 suffix
4. Verify sticky_session=true setting
worker.lb_worker.sticky_session=true
5. Session loss due to session expiry or Tomcat restart
→ sticky_session_force=false (allow fallback to other node)
Apache Configuration Validation Automation
# Check configuration syntax
sudo apache2ctl configtest
# Verify loaded modules
apache2ctl -M | grep -E "proxy|jk|headers|rewrite|ssl"
# List VirtualHosts
apache2ctl -S
# Trace actual request handling path (Apache 2.4)
# mod_remoteip trace
Proxy Request Logging
# Proxy request/response log format
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D" combined_timing
# Log slow requests exceeding 3000ms (3 seconds)
# (Use a custom log analysis script)
CustomLog ${APACHE_LOG_DIR}/access.log combined_timing
# Top 10 slowest requests (by processing time)
awk '{print $NF, $0}' /var/log/apache2/access.log | sort -rn | head -10
# Extract only 5xx error requests
grep ' 5[0-9][0-9] ' /var/log/apache2/access.log | tail -50
# Request count per hour
awk '{print $4}' /var/log/apache2/access.log | \
cut -d: -f2 | sort | uniq -c | sort -rn
Maintenance Mode (Graceful Maintenance)
# Maintenance page configuration
<VirtualHost *:443>
ServerName example.com
# Return 503 if maintenance flag file exists
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/maintenance.flag -f
RewriteCond %{REQUEST_URI} !=/maintenance.html
RewriteRule ^ /maintenance.html [R=503,L]
ErrorDocument 503 /maintenance.html
Alias /maintenance.html /var/www/maintenance.html
</VirtualHost>
# Start maintenance
touch /var/www/html/maintenance.flag
# End maintenance
rm /var/www/html/maintenance.flag
Security Hardening Checklist
# Hide server information
ServerTokens Prod # Expose only "Apache" (hide version)
ServerSignature Off # Remove server info from error pages
# Block unnecessary HTTP methods
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
# Block directory browsing
<Directory />
Options -Indexes
AllowOverride None
</Directory>
# Slowloris defense
RequestReadTimeout header=20,MinRate=500 body=20,MinRate=500
Quick Reference: Diagnostic Commands
# 1. Check port status
ss -tlnp | grep -E "80|443|8080|8009"
# 2. Validate Apache configuration
sudo apache2ctl configtest && echo "OK"
# 3. Verify modules
apache2ctl -M | grep -E "proxy_http|proxy_ajp|jk"
# 4. Test Tomcat connection
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/
# 5. Test Apache → Tomcat proxy path
curl -s -o /dev/null -w "%{http_code}" http://localhost/
# 6. Check SSL certificate expiry
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates
# 7. Real-time error log monitoring
sudo tail -f /var/log/apache2/error.log
Summary
| Item | Tool/Method |
|---|---|
| mod_jk status monitoring | /jkstatus handler + worker.jkstatus.type=status |
| Integration debugging | JkLogLevel debug / LogLevel proxy:debug |
| Connection pool tuning | connection_pool_size / max=100 ttl=300 |
| Sticky session verification | JSESSIONID .tomcat1 suffix + jvmRoute |
| Configuration validation | apache2ctl configtest + apache2ctl -M |
| Slow request analysis | %D log format + awk script |
| Maintenance mode | maintenance.flag file + RewriteCond |
| Security hardening | ServerTokens Prod + RequestReadTimeout |