If you are like me and use several different stacks for your projects, you may run into the problem of wanting to run multiple servers on same box/IP and have them receive HTTP requests on the same ports.
Let's say you have a box that hosts PHPApp using (Apache) HTTPD. Now you have the awesome new Java web app JavaApp that you want to host with Tomcat on the same box. You don't want to have to include Tomcat's port numbers in your URLs, but HTTPD is already using the default HTTP(S) ports (80/443). We want requests to http://javaapp.website.com
to go to Tomcat/JavaApp and requests to http://phpapp.website.com
to go to HTTPD/PHPApp. We want HTTPD and Tomcat to respond to requests on the same ports.
TCP only allows one listener per port. If you configure both HTTPD and Tomcat to listen on the same port, when you try to start the second server you will get an error complaining about the port not being available.
To solve this problem, you can pass (proxy) requests from the server listening on the ports you want to the server listening on alternate ports. Because HTTPD's default ports are the ports we want to use, I will use HTTPD to forward requests to Tomcat. I also want only requests sent to javaapp.website.com
to be proxied to Tomcat.
For this tutorial I will assume HTTPD is listening on ports 80 and 443 (HTTPS) and Tomcat is listening on ports 8080 and 8443 (HTTPS).
Reverse Proxies
When you setup a server as a reverse proxy, you are telling that server to act as a middle man between the client and another (origin) server for some requests.
- The proxy server gets an HTTP request.
- If some criteria are met, the proxy server passes (proxies) the request to the origin server.
- The origin server gets the request.
- The origin server creates a response.
- The origin server sends the response to the proxy server.
- The proxy server passes (reverse proxies) the response to the client.
In our case, HTTPD will be our proxy server, Tomcat will be our origin server, and our criteria for passing is if the request was sent to javaapp.website.com
.
- HTTPD gets an HTTP request.
- If the host of the request is
javaapp.website.com
, HTTPD passes (proxies) the request to Tomcat. - Tomcat gets the request.
- Tomcat creates a response.
- Tomcat sends the response to HTTPD.
- HTTPD passes (reverse proxies) the response to our client.
From the client's view, a reverse proxy server operates and appears as a normal server.
Setup HTTPD
The first step is to create a virtual host (vhost) to tell HTTPD which requests to proxy and to where.
cd
into your HTTPD vhost directory and create a vhost config (EX: javaapp.conf
). The EC2 default is /etc/httpd/config/vhost/
. If you don't have a vhost directory, you may have a single vhosts config file or your virtual hosts may be in your httpd.conf
.
Create a virtual host for the requests you want to proxy to Tomcat using the ProxyPass
module.
<VirtualHost *:80>
ServerName javaapp.website.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/javaapp/
ProxyPassReverse / http://127.0.0.1:8080/javaapp/
</VirtualHost>
This example will proxy all HTTP requests to javaapp.website.com
on port 80 to our local Tomcat server. http://javapp.website.com/path/page.html
will be proxied to http://127.0.0.1:8080/javapp/path/page.html
. Tomcat is running on the same box (127.0.0.1/localhost) and is listening on port 8080, so it will get the request. Tomcat will create the response and send it back to HTTPD. HTTPD will then reverse proxy (pass) the response to the client.
javaapp/
does not have to be included in the origin server URL, but I usually include it so I don't have to include the name of the Java webapp in the URL (http://javaapp.website.com/javaapp/path/page.html
vs http://javaapp.website.com/path/page.html
)
If you get a 503 (service unavailable) error, that means HTTPD is unable to send requests to your Tomcat server. Make sure Tomcat is running, accessible, and accepting requests. You can test with wget http://127.0.0.1:8080
from your box.
ProxyPreserveHost On
ensures the Host
header of the HTTP request remains its original value (subdomain.website.com
) and is not re-written to the proxied host (127.0.0.1
).
Setup Tomcat
There is no setup or configuration needed for Tomcat. Make sure that Tomcat can accept requests from localhost (127.0.0.1). Requests proxied to Tomcat from HTTPD are the same as any normal request Tomcat would receive from a client.
SSL
If you have SSL setup, and you want to support proxing HTTPS requests, some additional parameters are required.
<VirtualHost *:80>
ServerName javaapp.website.com
Redirect permanent / https://javaapp.website.com/
</VirtualHost>
<VirtualHost *:443>
ServerName javaapp.website.com
SSLEngine On
SSLCertificateFile certs.www/*.website.com.crt
SSLCertificateKeyFile certs.www/*.website.com.key
SSLProxyEngine On
ProxyPreserveHost On
ProxyPass / https://127.0.0.1:8443/javaapp/
ProxyPassReverse / https://127.0.0.1:8443/javaapp/
</VirtualHost>
The first virtual host is optional. It redirects all HTTP requests on port 80 to HTTPS requests on port 443.
The normal SSL directives are used with the addition of SSLProxyEngine ON
which enables the ability to proxy SSL requests. Without this directive you may see the error SSL Proxy requested for website.com but not enabled
.
If your Tomcat server is local like mine, it is not necessary to proxy with an HTTPS request. SSL protects data while in transit. Because the Tomcat server is local, requests proxied from HTTPD to Tomcat do not leave your box and do not need to be secured. However, if your Tomcat ports are open, users will be able to access your server unsecured via that port (http://javaapp.website.com:8080
).
If you do proxy using HTTPS requests, note that (by default) the origin server's SSL certificate is neither validated nor verified. This behavior can be changed using the SSLProxyVerify
directive. This shouldn't be an issue since we are proxying to our own server.