Heroku and HTTPS
Posted on 2017-03-15 by xkollar
.Heroku is a Platform as a Service provider
we have used in work for prototyping some small services. What bugged me though was
that even though they provide you with free HTTPS certificate for your *.herokuapp.com
site, they do not provide simple way to enforce HTTPS.
So I have wrote simple wai Middleware, which will take care of redirecting users to secured version of your site and also set HTTP_Strict_Transport_Security.
herokuHttps :: Middleware
= strictTransportPolicy . herokuHttpsRedirect
herokuHttps
isHerokuHttps :: Request -> Bool
= (Just "https" ==) . fmap CI.mk
isHerokuHttps . lookup "X-Forwarded-Proto" . requestHeaders
strictTransportPolicy :: Middleware
= ifRequest isHerokuHttps . modifyResponse
strictTransportPolicy $ mapResponseHeaders ((header, value):)
where
= "Strict-Transport-Security"
header = "max-age=31536000; includeSubDomains"
value
herokuHttpsRedirect :: Middleware
#if MIN_VERSION_wai(3,0,0)
herokuHttpsRedirect app r respond#else
herokuHttpsRedirect app r#endif
| isHerokuHttps r = runApp
| Just host <- hostHeader r = res . redir
$ "https://" <> host <> rawPathInfo r <> rawQueryString r
| otherwise = runApp
where
-- Heroku does not use "X-Forwarded-Host", as it simply preserves "Host"
hostHeader :: Request -> Maybe ByteString
= lookup "Host" . requestHeaders
hostHeader
redir :: ByteString -> Response
= responseLBS HTTP.status301 [("Location", url)] ""
redir url
#if MIN_VERSION_wai(3,0,0)
= respond
res = app r respond
runApp #else
= return
res = app r
runApp #endif
Many relevant Haskell libraries use wai (wai reverse dependencies so you can use it for example with your servant-based service.
PS: You really do not need this should you have proxy configuration under your control as all major reverse proxies can do this for you.