Making sure your users always connect to the HTTPS secured version of your site is a big concern these days. With search engines marking down non-HTTPS sites and users adopting tools to push their web browsing to the more secure option, it is something that developers and site owners need to take seriously.
At Fly, you’ll have already discovered that when you create an app, it appears as http://appname.fly.dev
and if you connect there, your browser switches automatically, by redirection, to https://appname.fly.dev
. That’s how we do it for the fly.dev domain.
Things are different when you bring your own certificate along and attach it to your app. There’s no automatically activated HTTPS upgrading because we understand people like to have the freedom to choose how their connections behave.
So how do you make a connection upgrade to HTTPS with your own custom domain…
A Fly Connection’s Life
When an application connects to a Fly app, it looks up the domain, gets an IP address back and connects to the IP address. With Fly, those IP addresses are AnyCast IP addresses and the connection will be routed to the nearest edge of the Fly network.
These Fly network edges are made up of proxies which handle the incoming connection. When you define handlers for an app (in fly.toml), it’s at this edge where the handlers step in to manage the “http” or “tls” components of the connect.
Once the edge proxy has located the nearest server and datacenter where your app has an instance running, the proxied connection is routed to that server. This stage of the connections journey take place over encrypted networking so the connection is made unencrypted.
Both https and http connections are sent to the internal port of the app. The app then responds and the proxy handles the response appropriately. If you wanted to upgrade all your HTTP connections to HTTPS, the appropriate response would be to redirect the client to the HTTPS version of the URL.
Detecting HTTP Connections
You may wonder “But if all connections are flattened out to unencrypted HTTP, how do we spot actual HTTP connections?”. That’s where being routed through a proxy comes into it. Proxies can add information to the connection about where the original connection came from.
These usually appear in the HTTP header and make up part of what we call the Fly runtime environment. The one header we are interested in is X-Forwarded-Proto
. It contains the protocol the incoming connection used - ‘http’ or ‘https’.
So, at its simplest, if our incoming connection to a has X-Forwarded-Proto: http
then we Redirect (HTTP Status 301) to the same URL but with https
in its header. All connections are then automatically upgraded.
Practical Upgrading - Go
Here’s a recent example of doing this in practice. We’ve been using an excellent lightweight Go server (PierreZ’s goStatic). It deals entirely in http connections which works great with the Fly architecture but we wanted to get it to upgrade non-“fly.dev” connections. This is a shorter version of the Go code we added
func handleReq(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Forwarded-Proto") == "http" {
http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
return
}
h.ServeHTTP(w, r)
})
}
Practical Upgrading - Node (and Express)
For Node (and Express), the same effect can be achieved with a little bit of Express middleware added to the stack:
app.use (function (req, res, next) {
if(req.get("X-Forwarded-Proto")=="http") {
// request was via http, so redirect to https
res.redirect('https://' + req.headers.host + req.url);
} else {
next();
}
});
Practical Upgrading - With Other Apps
In-App Upgrading like this is a great way to always ensure that where-ever your app is deployed. You can allow other applications to do the redirection work for you, like Nginx. Mix in something like:
if ($http_x_forwarded_proto = "http") {
return 301 https://$server_name$request_uri;
}
into your nginx.conf
and you’ll be upgrading your connections in the same way. This would, for example, but useful in a custom domain proxy, ensuring that all the customer’s users were getting a secure connection.
Beyond manual Upgrading
Even if you are not on Fly, you can make use of this technique, though rather than checking the request’s headers, you’ll need to check the request itself and the URL’s schema to identify if it’s an HTTP connection.
Back on Fly, we are looking at ways that we can also automate this process, without forcing a default behavior on all our users. We’ll get back to you on that when it’s ready; until then we think these redirection tips will help keep your apps securely connected.