Let Heroku do HTTPS, you do HTTP
The Heroku router will be the entry point that does the actual HTTPS communication with the client, then the router will forward the calls to your server (Jetty in my app) over HTTP. This means that the data is unencrypted between the Heroku router and your app, but if you are not a tin-foil-hat kind of guy, you'll be OK with that.
But those pesky users are accessing my app using HTTP
Here comes the problem, the app is available both through HTTP and HTTPS, and while you aren't into tinfoil hats, you don't want your non-IT-savy users' credentials freely available for anyone with Firesheep on that insecure internet cafe WIFI. What you want to do is to enforce HTTPS. But how do you do that? Your app is configured to run HTTP, not HTTPS.
Developer, meet x-forwarded-proto
Well, Heroku sets some header values that indicate wether your app is being accessed using HTTP or HTTPS, say hello to x-forwarded-proto. This header is set by the Heroku router, and will be either HTTP or HTTPS. So, by querying that value, you'll be able to see how your app was accessed. We now have a way to figure out how the app is accessed, all that is left to do now is to enforce HTTPS.
The following extractor will help you figure out what protocol was used to access your app
object XForwardProto extends StringHeader("x-forwarded-proto")
You can now use it in your Plans like this:
case r@POST(Path(Seg("login" :: Nil))) & XForwardProto("https") => //Do loginto only respond to logins over HTTPS for example.
Force HTTPS
What would be even better than only responding to https would be to redirect all http traffic to https. To do that I wrote (or to be fair, it was mostly my good colleague Erlend's work) this HerokuRedirect object:
object HerokuRedirect { def apply[A, B](req: HttpRequest[A], path: String): ResponseFunction[B] = { val absolutepath = if (path.startsWith("/")) path else "/" + path req match { case XForwardProto(_) & Host(host) => Found ~> Location("https://%s%s".format(host, absolutepath)) case _ => Redirect(absolutepath) } }Now, you can add to your Plan (preferably the first case of your first Plan in Unfiltered) a redirect for all incoming http requests, like this:
case r@GET(_) & XForwardProto("http") => HerokuRedirect(r,r.uri)Quite nice, hu?
More uses for the XForwardProto
I'm also using the XForwardProto when setting cookies, specifically when setting the secure flag, like this:
val secure = req match { case XForwardProto("https") => Some(true) case _ => Some(false)} SetCookies(Cookie(name = "someKey", value="some value", secure = secure)) ~> Html5(Of course I could have set the secure flag always, but then testing on localhost would be a pain, this way I can test without https on localhost, and still enforce https only on heroku.Some text
)