Omgo's Blog

May 7, 2010

Apache rewrite rules for multi tenant applications

Filed under: General — aswin @ 3:22 am

We have a multi-tenant web application that serves customizable page content based on the subdomain value or the attribute set for the currently logged in user. We have a generic context root for this particular application that appears in the browser bar and looks something like https://client.example.com/online where “online” is the context root. The internally available servlet container (Tomcat) has the web application configured under this context root and we use Apache HTTP server to reverse proxy the requests to this Tomcat instance. Each client had a virtual host mapping based on the subdomain, and apache would rightfully pass in this subdomain value as part of the query string so that the backend application can customize contents if need be.

One of the clients we were onboarding had the hard requirement that the context root itself be changed to a custom value, say “custom” in addition to having a sub domain. i.e they wanted the url of the form https://client.example.com/custom and not the generic one https://client.example.com/online. The application was already in production and we could not make any changes to it at all, leaving us with the only option of making the changes at the webserver level (Apache HTTP server). The web application appends the context root as part of the URLs (we are using grails and the urls are generated with the context root) and in form post action. So we had to come up with some rewrite rules for handling this situation and used it along with the the mod_proxy and mod_proxy_ajp modules. We wanted the url in the address bar to have the “/custom” context root and was pretty easy with the rewrite rules. The mod_rewrite would just redirect a request for “/online” to “/custom”, but for POST requests this was an issue as a redirect would cause the POST value to be lost. So we had to just allow ‘POST’ alone to proxy through to the backend without any rewriting and was fine as the application logic followed PRG pattern for avoiding duplicate form submission. Since these redirect request were ‘GET’ request it would be matched by the rewrite rules and redirected again to the correct value (‘/custom’), that is redirected twice; once by the application and then by the Apache server.

Here are the relevant part of the configuration in case if any one feel the need to do it. This whole configuration goes inside a element in our configuration .

    ProxyRequests Off
      <Proxy *>
         Order deny,allow
         Allow from all
      </Proxy>

      RewriteEngine on

      #if url ends in client.example.com , redirect to client.example.com/custom
      RewriteRule ^(/)?$ /custom/ [R,L]

      # if the request is anything but post and is for client.example.com/online change it to /custom and redirect. 
	  # For post to /online we cannot redirect, as it would cause post data to be lost. (Remember the application has all the links and post form  urls coded
      # for /online, so we are trying to transparently handle these).
      #  This also means that there is a possiblity of the browser displaying online as result of a http post, but in most places its
      #  a redirect after post giving this rule a chance to redirect again to the correct url (/custom)
      RewriteCond %{REQUEST_METHOD}  !^POST$
      RewriteRule ^/online/(.*)$ /custom/$1 [R]

      # if this is here, its a post request, so allow it to proxy through to backend webapp at /online
      RewriteRule ^/online(?:/(.*)$)? ajp://localhost:8102/online/$1 [P,L,QSA]
    
	  # all request aimed specifically at /custom should be passed through 
      RewriteRule ^/custom(?:/(.*)$)? ajp://localhost:8102/online/$1 [P,L,QSA]
	        
      # ProxyPassReverse rewrites the redirect headers created by the backend webserver to the correct form, otherwise there would be an unnecessary hop
	  # The url here is what the ProxyPassReverse sees, so put it as it is. Most of the articles in the net gives this value wrongly and suggests using 
	  # the url ajp://localhost:8102..... and it does not work. Check the logs after setting the LogLevel to debug to see what the ajp connector is 
	  # returning for the Location header, which is what this function (ProxyPassReverse) rewrites. 
      ProxyPassReverse /custom/ https://client.example.com/online/

      #make sure the cookie domain is set to /, as we want interoperatbility between subdomains (custom and online). If not present there could be 
	  # funny side effects such as having to relogin or missing data etc.
      ProxyPassReverseCookiePath /online /

      #If stuff goes wrong, uncomment to enable detailed logging
      #LogLevel debug
      #RewriteLog "/etc/httpd/logs/rewrite.log"
      #RewriteLogLevel 5
Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: