Fronting Liferay Tomcat with Apache HTTPd daemon Revisted

Introduction

So originally I presented the blog post, Fronting Liferay Tomcat with Apache HTTPd daemon, but that post featured my partiality for using mod_jk to connect HTTPd and Tomcat.

Personally I think it is much easier to do the JkMount and JkUnmount mappings to point to Tomcat, plus Liferay sees the original request so when it generates URLs, it can generate them using the incoming request URL.

Remember that Liferay generates URLs for other assets such as stylesheets, javascripts, images, etc. Rather that being hard-coded, Liferay will create URLs based on the incoming request URL. This should mean that the host, port and protocol it uses for the generated URLs will correctly resolve back to the Liferay server.

When you front with HTTPd, it gets the actual request, not Tomcat. If you use the AJP binary protocol with mod_jk, the original URL goes to Liferay and it can generate URLs using the normal logic.

But when you use mod_proxy, things can be challenging. The URL that goes to Tomcat and Liferay is the URL request from HTTPd, not the external client browser. So if you are proxying from HTTPd to localhost:8080, a defaut Liferay configuration will end up generating URLs with localhost:8080 in them even though they will not be valid in the external client browser.

The challenge is getting the protocol, host and port correct when Liferay generates URLs.

Option 1 - Handle It In Liferay

The first way to get this right is to do it in Liferay.  By setting the following properties in portal-ext.properties, you control how Liferay generates URLs:

# Ports that apache HTTPd will be listening on
web.server.http.port=80
web.server.https.port=443
 
# Host name to use in generated URLs, will typically be the name used to get to HTTPd
web.server.host=www.example.com
 
# Force all generated URLs to use the HTTPS protocol
# If not set, will use the protocol from the connection from HTTPd to Tomcat.
web.server.protocol=https

As specified above, regardless of the incoming request URL, Liferay will generate URLs in the form of https://www.example.com/...

In your httpd.conf file, all that is missing is the ProxyPass directives to send traffic to the local tomcat instance. Personally I want to send all traffic to Tomcat and specifically exclude those paths that should not go to Tomcat. I find this to be easier to manage rather than trying to figure out all URL patterns that Liferay might use to send traffic selectively to Tomcat:

# Serve /excluded from the local httpd data
ProxyPass /excluded !
 
# Pass all traffic to a localhost tomcat.
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/
 
# This would be the configuration to invoke a tomcat on another server
# ProxyPass / http://192.168.1.6:8080/
# ProxyPassReverse / http://192.168.1.6:8080/

This is a very, very simplistic HTTPd configuration. It doesn't deal at all with virtual host configurations, http/https configurations, etc.

Also on the Liferay side it will generate URLs w/ www.example.com and may not use the Liferay configured virtual hosts correctly either.

Option 2 - Handle It In HTTPd

The second way to handle things is to push the heavy lifting to HTTPd.

We start by configuring httpd.conf (and the child files) to use the Virtual Hosts:

<VirtualHost *:80>
    # Set the header for the http protocol
    RequestHeader set X-Forwarded-Proto "http"
    
    # Serve /excluded from the local httpd data
    ProxyPass /excluded !
		 
    # Preserve the host when invoking tomcat
    ProxyPreserveHost on
		 
    # Pass all traffic to a localhost tomcat.
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
		 
    # This would be the configuration to invoke a tomcat on another server
    # ProxyPass / http://192.168.1.6:8080/
    # ProxyPassReverse / http://192.168.1.6:8080/
</VirtualHost>
 
<VirtualHost *:443>
    # Set the header for the https protocol
    RequestHeader set X-Forwarded-Proto "https"
    
    # Serve /excluded from the local httpd data
    ProxyPass /excluded !
		 
    # Preserve the host when invoking tomcat
    ProxyPreserveHost on
		 
    # Pass all traffic to a localhost tomcat.
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
		 
    # This would be the configuration to invoke a tomcat on another server
    # ProxyPass / http://192.168.1.6:8080/
    # ProxyPassReverse / http://192.168.1.6:8080/
</VirtualHost>

So this sets up two virtual hosts, but using wildcards so they will actually handle all incoming requests that get to HTTPd. Each virtual host is tied to the HTTP or HTTPS protocol and set a header, X-Forwarded-Proto with the protocol to use.

Also it includes the ProxyPreserveHost directive which will preserve the incoming host when sending the request to Tomcat (it will get the incoming host instead of the localhost from the ProxyPass directive).  The X-Forwarded-Host header will also be set.

On the Liferay side, the portal-ext.property changes are simpler since we didn't change the expected header names:

#
# Set this to true to use the property "web.server.forward.host.header" to
# get the host. The property "web.server.host" must be set its default
# value.
#
web.server.forwarded.host.enabled=true

#
# Set this to true to use the property "web.server.forward.port.header" to
# get the port.
#
web.server.forwarded.port.enabled=true
 
#
# Set this to true to use the property "web.server.forward.protocol.header"
# to get the protocol. The property "web.server.protocol" must not have been
# overriden.
#
web.server.forwarded.protocol.enabled=true

This configuration elegantly handles virtual hosts correctly in HTTPd and in Liferay, it respects the incoming protocol correctly (to support mixed mode requests) and it is just easy to set up and validate, plus it won't require property changes when you add a new virtual host to Liferay.

Conclusion

So here's two options for configuring your server to support fronting Tomcat with HTTPd but using mod_proxy instead of mod_jk.

Personally I recommend using mod_jk, but if I had to go with mod_proxy, I would lean towards implementing it using Option 2 above.

 

Blogs