Putting Apache HTTP Server in front of Tomcat lets a Java web application answer on a normal site hostname while the Tomcat connector stays on an internal port. The public virtual host can handle TLS, logs, compression, and access policy before forwarding only the application paths that belong to Tomcat.
Apache uses mod_proxy and mod_proxy_http to pass HTTP requests to the backend connector. ProxyPass sends the matching public path to Tomcat, ProxyPassReverse rewrites backend redirect headers, and ProxyPreserveHost can pass the public Host header to the application.
Keep the public path and the Tomcat context path aligned unless the application was designed for a different external path. The examples below use the Debian and Ubuntu Apache layout with /etc/apache2, apache2ctl, and the apache2 service; on Red Hat-style systems, use the matching /etc/httpd path, apachectl command, and httpd service name.
Related: Configure Apache as a reverse proxy
Related: Test Apache configuration
Related: How to configure RemoteIpValve in Tomcat
Tool: Apache Virtual Host Generator
$ curl -isS http://127.0.0.1:8080/ HTTP/1.1 200 Content-Type: text/html Content-Length: 38 Tomcat backend reached through Apache
Run this check on the server that will run Apache. A Tomcat URL that works from a workstation can still be blocked from the reverse proxy host by a listener, firewall, or bind-address setting.
$ sudo a2enmod proxy proxy_http Enabling module proxy. Considering dependency proxy for proxy_http: Module proxy already enabled Enabling module proxy_http. To activate the new configuration, you need to run: service apache2 restart
proxy and proxy_http perform the HTTP backend handoff from Apache to Tomcat.
$ sudoedit /etc/apache2/sites-available/app.conf
Use a short file name tied to the public application name so later operators can identify the proxy vhost from /etc/apache2/sites-available without opening every file.
<VirtualHost *:80> ServerName app.example.net ProxyRequests Off ProxyPreserveHost On ProxyPass "/" "http://127.0.0.1:8080/" ProxyPassReverse "/" "http://127.0.0.1:8080/" ErrorLog ${APACHE_LOG_DIR}/app.error.log CustomLog ${APACHE_LOG_DIR}/app.access.log combined </VirtualHost>
Keep the same trailing slash style on both sides of each mapping. For a context path such as /inventory/, use that path on both the public and backend side instead of hiding a different Tomcat context behind Apache.
Do not turn ProxyRequests on for this reverse proxy. Forward proxy mode can let clients use the server to reach arbitrary external sites.
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" proxyName="app.example.net" proxyPort="80" scheme="http" />
For an HTTPS public virtual host, use proxyPort="443", scheme="https", and secure="true". For multiple public hostnames or client-IP-sensitive controls, configure a trusted forwarded-header path instead. Related: How to configure RemoteIpValve in Tomcat
$ sudo a2ensite app.conf Enabling site app. To activate the new configuration, you need to run: service apache2 reload
Disable 000-default.conf if it would otherwise answer unmatched requests on the same listener.
$ sudo apache2ctl -t Syntax OK
If AH00558 appears before Syntax OK on a fresh host, Apache is warning about a missing global ServerName. Fix that warning separately, but Syntax OK means this vhost file parsed successfully.
Related: How to test Apache configuration
$ sudo systemctl restart tomcat10
Skip this restart when the Apache vhost was the only file changed.
$ sudo systemctl reload apache2
Use sudo systemctl reload httpd on Red Hat-style systems when Apache runs under the httpd unit.
$ curl -isS http://app.example.net/ HTTP/1.1 200 Date: Wed, 10 Jun 2026 20:57:50 GMT Server: Apache/2.4.66 (Ubuntu) Content-Type: text/html Content-Length: 38 Tomcat backend reached through Apache
The Server header shows Apache answered the client-facing request, while the body should match the Tomcat application endpoint.
$ sudo cat /var/log/apache2/app.access.log 203.0.113.25 - - [10/Jun/2026:20:57:50 +0000] "GET / HTTP/1.1" 200 265 "-" "curl/8.18.0"
If you also need Tomcat-side request rows for backend timing or client-IP verification, enable and review a Tomcat AccessLogValve for the application host. Related: How to enable Tomcat access logs