Matt RaibleMatt Raible is a Web Architecture Consultant specializing in open source frameworks.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Apache 2 on OS X: Configuring mod_proxy and SSL

I recently had to setup Apache as a front-end web server for multiple backend servlet containers. The backend containers serve up different web applications, and the Apache front-end unites them from a hostname and port standpoint. The following instructions describe how to configure Apache 2 on Mac OS X to proxy requests to Tomcat or Jetty running on localhost:8080. It also shows how to enable SSL on Apache and force it for certain URLs in your Java web application.

Apache comes pre-installed on OS X, so you should be able to start it by enabling "Web Sharing" in System Preferences > Sharing.

$APACHE_HOME on Leopard is /etc/apache2. On Tiger, it's /etc/httpd. If you've upgraded Tiger to Leopard, it's likely you'll have both directories so make sure you're modifying the right one. I lost a few hours figuring this out, so hopefully this knowledge will appease some googler in the future.

Configuring mod_proxy

  1. Open $APACHE_HOME/httpd.conf and add the following on line 480 - at the very bottom, just before "Include /private/etc/apache2/other/*.conf".
    #
    # Proxy Server directives. 
    #
    <IfModule mod_proxy.c>
        ProxyRequests On
        ProxyPreserveHost On
    
        ProxyStatus On
        <Location /status>
            SetHandler server-status
    
            Order Deny,Allow
            Deny from all
            Allow from 127.0.0.1
        </Location>
    
        ProxyPass    /myapp    http://localhost:8080/myapp
    </IfModule>

    ProxyPreserveHost allows request.getServerName() and request.getServerPort() to work as if there is no proxy server in place. In other words, even though Tomcat is running on 8080, request.getServerPort() will return 80.

  2. The most important line is the last one as this is the dictates the location of your applications. Add more lines as you need to add more applications.
  3. If everything is configured correctly, you should be able to run sudo apachectl restart and navigate to http://localhost/status. If you receive a "forbidden" error, make sure your /etc/hosts has an entry mapping 127.0.0.1 to localhost (as one of the last entries), or change "Allow from 127.0.0.1" to "Allow from localhost". If you get a "Server not found" error, you can tail the error log at "/var/log/apache2/error_log".

One issue I've seen with mod_proxy is when a request comes in and the backend server is down. When this happens, Apache returns a 503 Service Temporarily Unavailable and it doesn't seem to go away after the backend server is restarted. It does resume proxying after a while, but I haven't determined what causes the proxy to come back to life. If you know a setting that forces mod_proxy to check for the backend server on every request, please let me know.

Configuring SSL

  1. Open $APACHE_HOME/httpd.conf and uncomment the following on line 470:
    Include /private/etc/apache2/extra/httpd-ssl.conf
  2. Open $APACHE_HOME/extra/httpd-ssl.conf and change line 78 to:
    ServerName localhost:443
  3. In httpd-ssl.conf, change line 99 to:
    SSLCertificateFile "/private/etc/apache2/ssl.key/server.crt"
  4. In httpd-ssl.conf, change line 107 to:
    SSLCertificateKeyFile "/private/etc/apache2/ssl.key/server.key"
  5. In httpd-ssl.conf, add the following after SSLEngine on to allow proxying via HTTPS:
    SSLProxyEngine on
  6. Follow the Using mod_ssl on Mac OS X tutorial. For "Common Name/Server Name", use "localhost". You can download the source for mod_ssl (which you need at one point during the tutorial) at http://www.modssl.org/source/.
  7. Run sudo apachectl restart and go to https://localhost. If you get a "Server not found" error, run sudo apachectl -t to verify the syntax of your config files or tail -f /var/log/apache2/error_log to verify there are no errors in the log files.

Forcing HTTPS for certain URLs
If you proxy requests from /myapp -> http://localhost:8080/myapp, request.isSecure() will return false. If you change it to /myapp -> https://localhost:8443/myapp, request.isSecure() will return true. I needed to figure out a way to have http://localhost/myapp go to http://localhost:8080/myapp and https://localhost/myapp to go http://localhost:8443/myapp. Even better, I wanted to configure things in a way so request.isSecure() returned the value based on the originally requested URL, not on the proxied URL. Configuration like the following would be ideal:

ProxyPass    http://*/myapp    http://*:8080/myapp
ProxyPass    https://*/myapp   https://*:8443/myapp

The solution I came up with is to standardize on secure URLs in my application. That is, use /secure/* as a prefix for all URLs that should be accessed via SSL. To follow this convention and force it, I added the following in my application's web.xml file:

<security-constraint>
  <web-resource-collection>
    <web-resource-name>Secure Area</web-resource-name>
    <url-pattern>/secure/*</url-pattern>
  </web-resource-collection>
  <user-data-constraint>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
  </user-data-constraint>
</security-constraint>

Once this is in place, accessing http://localhost/myapp/secure/index.html will result in an error. Accessing it using https will succeed. Following this, you can change your ProxyPass rules to the following and all requests to /secure/* will be https; other requests will be sent to http. The order of the rules below is important.

ProxyPass    /myapp/secure   https://localhost:8443/myapp/secure
ProxyPass    /myapp          http://localhost:8080/myapp

If this isn't a good strategy for you, Tomcat has the ability to use a redirectPort (in server.xml) that auto-redirects from http to https when CONFIDENTIAL is used in web.xml. I'm not sure if this redirect will carry through values from a form post.

Posted in Open Source at Apr 24 2008, 10:58:03 AM MDT 8 Comments
Comments:

I was wondering... why did you choose mod_proxy and not mod_jk?

Posted by Abel Muiño on April 28, 2008 at 01:32 AM MDT #

And in Apache 2.2 there is also mod_ajp, although I admit, I haven't used it with ssl so far.

Posted by Mauli on April 29, 2008 at 09:57 AM MDT #

I'm don't know how if this is true for for tomcat as well but at least for jetty the documentation says "Using the HTTP Connectors is greatly preferred, as Jetty performs significantly better with HTTP and the AJP protocol is poorly documented and there are many version irregularities."

Posted by Kai Grabfelder on April 29, 2008 at 10:37 AM MDT #

Hi Matt,

We have been using that for a while and I would add some notes:

.- You might also want to use ProxyPassReverse so redirects by the backend server are handled correctly, even though it is not mandatory.

.- In order to force SSL requests to go to a different server, you simply need to define a virtual host based on the port, and have the different proxy settings on each virtual host. This way you can have the same URL scheme in both circumstances. Just in case someone wondered, proxying to an HTTPS backend does not usually work, as mod_proxy does not understand HTTPS (or I have been unable to get it to work ;) ).

.- Mod_proxy just works for "directory wide" settings, but if you want to proxy certain files (*.jsp, *.myapp for example), mod_redirect can be used with the P or PT settings, or it can be used in combination with mod_proxy.

.- The 503 and 502 error status can also be helpful if you intercept them and show something along the lines of "application is being mantained, sorry for the inconveniece... blah blah" This way, when in maintenance mode you simply stop your container or change the port in the proxy setting & reload apache config and the message is displayed automatically.
...
Regarding mod_proxy vs mod_jk, we use mod_proxy because we can then use the containers we want, mixing versions, changing from one version to another, from one container vendor to another...without bothering with the native libraries required for each container/version. Yes, some performance is lost, but in our case (20+ applications) flexibility is preferred. Other people might have other preferences.

S!

Posted by GreenEyed on April 30, 2008 at 12:25 AM MDT #

Hey Matt, great post. Getting Apache to do everything on Mac OS X is sometimes a real pain. This helps. I wrote a blog post along the same lines here: http://blog.lo-fi.net/2007/11/leopard-for-web-developer-multiple.html

Posted by Eric on June 27, 2008 at 06:17 PM MDT #

Hey Matt et al: I have an issue where I'm running a Facelets app on Tomcat with no problem. After implementing the proxy from Apache some CSS and Javascript files are not loaded. In FireFox nothing from RichFaces is loaded (CSS or JS). In Safari, my images, my CSS, and RichFaces are not loaded.

I tried to implement mod_proxy_html but I'm having difficulty because it requires libxml2.so and this file is not on my machine (MacBook Pro - OS X 10.5.6). I was able to build mod_proxy_html with like this:

apxs -c -I/usr/include/libxml2 -i mod_proxy_html.c 

But libxml2.so needs to be loaded in httpd.conf before mod_proxy_html.

Any pointers?

Posted by John on April 27, 2009 at 07:59 AM MDT #

"If you know a setting that forces mod_proxy to check for the backend server on every request, please let me know. "

From the doco (http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypass)

retry : Connection pool worker retry timeout in seconds. If the connection pool worker to the backend server is in the error state, Apache will not forward any requests to that server until the timeout expires. This enables to shut down the backend server for maintenance, and bring it back online later. A value of 0 means always retry workers in an error state with no timeout. Default value is 60 (seconds).

Example is:

ProxyPass    /myapp    http://localhost:8080/myapp retry=5

Forces ProxyPass to check back in 5 seconds after it detects an error state.

Posted by Richard on November 19, 2009 at 07:43 PM MST #

Use

ProxyRequests off

instead of:

ProxyRequests on

You don't want your server to become public proxy. Use Google for more details.

Posted by Mehi on February 16, 2010 at 08:02 AM MST #

Post a Comment:
  • HTML Syntax: Allowed