Play Framework: How to Host multiple sites using same Play instance (aka virtualhosts)

Lately I have coded few small services/sites using play framework and I found the need to co-host these multiple sites on a single physcial server.

The standard solution in play community is to run these play applications separately (each listening on a separate port) and then use apache or nginx or some other lightweight server as reverse proxy in front for routing (e.g., apache as front-end for play apps). It works, but it is too much overhead. Instead I wanted a single play instance running multiple sites -- similar to Apache virtualhost. Here is a quick guide on how I implemented this using Play framework (2.5.x).

Use Case - Single play instance serving two sites/domains say www.example1.com and www.example2.com

Solution -- in three easy steps:

Step 1: Create multiple route files one for each site

Each route file is in standard play format

# ls conf/
application.conf
prod.conf
example1.routes
example2.routes

Step 2: Extend DefaultHttpRequestHandler

We extend DefaultHttpRequestHandler to route requests to the right routes file based on the host/domain name in the incoming request.

Notes:

  1. Every route file created under conf folder can be referred using its auto-generated reference - e.g., example1.Routes and example2.Routes for our case.
  2. We use dependency injection (DI) to make our life easier

Sample code using scala:

package utils

import javax.inject._
import play.api.mvc.RequestHeader
import play.api.http.HttpConfiguration
import play.api.http.DefaultHttpRequestHandler
import play.api.http.HttpErrorHandler
import play.api.http._

class MultiSiteRequestHandler @Inject() (errorHandler: HttpErrorHandler,
                                         configuration: HttpConfiguration, 
                                         filters: HttpFilters,
                                         example1Router: example1.Routes, 
                                         example2Router: example2.Routes)
      extends DefaultHttpRequestHandler(example1Router, errorHandler, configuration, filters) {

        override def routeRequest(request: RequestHeader) = {
		    request.host match {
		      case  "www.example1.com" => example1Router.routes.lift(request)
		      case  "www.example2.com" => example2Router.routes.lift(request)
		      case _ => {
		        super.routeRequest(request)
		      }
		    }

        }
  }

Step 3: Tell play framework

We can tell play framework to use our newly defined class for request handling using following config:

# in application.conf
play.http.requestHandler = "utils.MultiSiteRequestHandler"

That's it and play is ready to host multiple sites using a single instance. We can add as many sites as we want using this approach

play-framework scala