Separate the Static from the Dynamic with Tomcat and Apache

Hosting servlets via Apache, mod_jk, Tomcat, mod_ssl and a few rewrite rules.

Hosting multiple Java Web-enabled applications with Apache/SSL in combination with Tomcat is potentially highly detailed. Separating the dynamic from the static content requires URL rewriting and aliases. This article discusses one viable configuration.

I describe the basics of how to host multiple Java Web applications using a pure Apache project approach. In other words, I explain how to apply Apache, mod_ssl, some rewrite rules and the Tomcat Servlet container to gain control of a consistent and viable production environment. In real life, I am a more-than-a-little-busy developer, and one of my more-recent tasks was to define and implement a structure to host a complex database-intensive Web-enabled searchable publication mechanism through the life cycle. I condense the experience gained and explain the most relevant details here.

The basics of placing an Apache Web server in front of multiple Tomcat servers is explained in an article by Daniel McCarthy on the Linux Journal Web site (see Resources). I take this article somewhat further by adding the ability to provide secure communication via SSL and show how to optimize performance by separating dynamic content, such as JSP pages, from static content, such as HTML and images. Further security issues also are nodded at briefly.


The following preparations are for those who want to generate a working instance of the infrastructure mentioned. This infrastructure involves a locally configured Apache server running with two Tomcat instances, all being referenced from a Web browser via different loopback (127.0.0.x) addresses. This article is still worth reading without following through with the recipe.

I assume that the following have been installed: Apache 1.3x Web server, mod_ssl, mod_jk and two instances of a Tomcat 5.5.x server, one running the ajp1.3 connector on the standard port of 8009 and the shutdown port of 8005, and the other on port 8019 and 8015. I have chosen a plain-old stable and reliable Apache 1.3.x server over an Apache 2.x version on the principle that you shouldn't fix what isn't broken. At the Institutes for which I have been responsible, during the past few years they have run Apache 1.3.x without issue, the system administrators have built up their knowledge, and the systems are maintained and patched to the highest levels and snuggly sit in the maturity section of the Web server's life cycle. The same applies for the choice of mod_jk over mod_jk2. In fact, mod_jk2 development has been discontinued due to the complexity of configuration.

If you have a Debian-based Linux distribution, to install the Apache server without compiling, try the following:

sudo apt-get install apache
sudo apt-get install libapache-mod-jk
sudo apt-get install libapache-mod-ssl

You should now have a running Apache instance with the configuration files sitting under /etc/apache.

For the Tomcat servers, you have two choices. The first is to use one instance of the binary and then two instances of the configuration, and then run a startup script that applies the unique instance of the binary with different configurations. The second choice is to use two copies of the Tomcat server and modify the server.xml file. The advantage of the first approach is the avoidance of replication of executable code. However, this is nearly always a false economy. The second approach has advantages for complex environments where you want to host different versions of Tomcat servers. The second approach is more relevant for Application Service Providers that have multiple customers. A division exists between code that is written for Java 1.5 that runs natively in Tomcat 5.5 (without installing the 1.4 compatibility package) and Java 1.4 that runs in Tomcat 5. Furthermore, the Servlet implementation is more up to date the newer the Tomcat version. Due to the current velocity of change, software that is hosted for more than a year can be considered legacy, so there always will be a demand for the use of older but still reliable servers.

Next, we want to test only on the loopback addresses with no packets reaching the network. This can be achieved by modifying the /etc/hosts file to something similar to: bronze_a silver gold

Therefore, every time you type https://bronze_a, no DNS lookups are necessary. The packets from the browser never will reach the Internet and will stay local to

In the main Apache configuration file, httpd.conf, you will find an include line that tells Apache to look under the conf.d directory for further configuration. For example:

Include /etc/apache/conf.d

Every time a package is installed that requires configuration changes for Apache, you will find an extra configuration file within the conf.d directory. In fact, if you want (for a nice aside), try to install Drupal and read the Drupal.conf file that is dumped.

I want to keep our work separate from the rest of the world's. No doubt, we will generate mistakes during playtime. Add a second line to include a directory for our virtual hosting files:

Include /etc/apache/vhosts

Then, make the directories /etc/apache/ssl and /etc/apache/vhosts. Later, we will place our certificates and server keys in the SSL directory, one set per virtual host.

Next, check the httpd.conf file to see whether the SSL engine is turned on. I want to turn the engine off until enabled per virtual host. So, the line SSLEngine On should change to SSLEngine Off.

Now we have an Apache 1.3.x server that is ready for action.

If you have not set up your Tomcat servers yet, you need to modify the following lines under the tomcat_root/conf/server.xml file for the second instance. Change the port numbers to 8015 for the shutdown command and port 8019 for the AJP/1.3 connector:

<Server port="8005" shutdown="SHUTDOWN"> 
<Connector port="8080"
<Connector port="8009"
       enableLookups="false"  protocol="AJP/1.3" />

For the sake of security, change the shutdown attribute from the value SHUTDOWN to some randomly long string. Otherwise, perhaps on the worst day under a badly defended system, a cracker can Telnet in and type SHUTDOWN, and then your server is down. Also, I would comment out the 8080 connector. There is no need to expose Tomcat directly to the Internet.

Only one task is left—to create two Web applications. Under the webapps directory of the first Tomcat instance, create a bronze_a directory, and then under that directory, create a WEB-INF directory. Place the following web.xml file in WEB_INF:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<web-app xmlns=""

    BRONZE_A Dynamic

Notice the mention of web-app_2_4.xsd. This web.xml file will not work under Tomcat 5, which uses the 2.3 standard. Under the webapps/bronze_a directory, place the following index.jsp file. This is our poor yet relevant example of dynamic content:

<%String mess="Hello World from Bronze_a"; %>
 <%=mess%> <br><%=request.getRequestURI()%>

Follow the same procedure for the second instance, but replace the string bronze_a with silver under the webapps/silver directory of the second Tomcat instance.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Chicken-and Egg problem

twinkeloogje's picture

About the proverbial chicken-and-egg problem, you say that "the cn attribute has to be defined with the value of the hostname of the target server". But by using X509 extensions, you can come by this problem. It is possible to specify some additional attributes on certificates, especially "subjectAltName".
OpenSSL supports and can handle these extensions but in its default configuration it needs a little tweaking, and I find it difficult to find some clear documentation on this.

The way we use them in our company (until we find some more generic solution) is by copying the default OpenSSL configuration before every certificate request (typically found in /etc/ssl/openssl.cnf) and editing the following sections :

[ req ]
req_extensions = v3_req

[ v3_req ]
extendedKeyUsage = serverAuth

In this example you can thus easily use "localhost" in the CN for your certificate.
Then you can generate you key request as follows : openssl req -new -out localhost.csr -config openssl_localhost.cnf

Of course you still need to have this certificate signed, so if you use your own OpenSSL CA, you have to adapt the configuration likewise (upon each invocation)
You can check if your certificate was correctly generated by using the following command: openssl x509 -noout -text -in localhost.crt

I don't know to what extent these certificate extensions are supported or honoured by commercial CAs, but if you ask, I think they should implement it.
Anyway, the generated certificates are being well understood by (at least) all programs that use the OpenSSL libraries, and also most browsers like Firefox, Internet Explorer and maybe others.
We use them for some of our web servers, ldap servers, and we even found some programs that can reimport these keys/certificates into a Java Key Store (*.jks)

The biggest advantage of this solution is that in environments with limited number of IPs available, e.g. at a hosting company, you can use multiple secure hostnames on a single IP address. Or when you need to host services in a high-availability scenario, with server daemons that can't use more than one certificate, you can use this solution to add the virtual service as an alternative hostname to the participants certificates, in addition to their respective real CNs. At the same time you can use their real individual names securely for monitoring and diagnostic purposes.

Jeroen Hautekeete