How To Deploy Canonical Hostname and HTTPS Redirects to edge servers using Fly

We recently changed hosting for initialized.com and were faced with what has become a very common task over the years, setting up redirects to our canonical domain and port, notably ensuring requests to the “www” subdomain and HTTP requests both redirect to https://initialized.com while preserving the request path. This is quite a simple task when you’re running your own web server, but with a statically generated site like ours we had to figure out somewhere else to host the redirection logic.

Immediately it struck me that this is a great use case for our portfolio company Fly's new Edge Apps. With a little bit of code we can setup all the redirection logic we need and then deploy our edge app to Fly’s fleet of edge nodes around the world ensuring low latency redirects from anywhere.

Fly edge apps are implemented as simple Javascript applications. At a high level the main requirement is to call the fly.http.respondWith with a function that takes a Request object and returns a Response object. Here is what that looks like for an edge app that redirects all HTTP requests to HTTPS:

fly.http.respondWith((req) => {
const url = new URL(req.url)
let redirectNeeded = false
  if (url.protocol !== 'https:') {
redirectNeeded = true
url.protocol = 'https'
url.port = '443'
}
  if (redirectNeeded) {
return new Response(
'Redirecting',
{
status: 301,
headers: { 'Location': url.toString() }
}
)
}

return new Response('No redirect needed!', { status: 200 })
})

Above we use a URL object to parse the incoming request URL which makes it simple to then check the protocol of the request. If it does not match https: we can update just the protocol and port of the URL and return a Response object with a redirect status and Location header. Otherwise we can issue a successful response. If our entire site was served by a Fly edge app (a good idea for other reasons) we would do something else for the final line of the function, like fetch the request from a Fly backend and proxy it through.

To deploy this we first need to create a Fly account at fly.io. Then, assuming we have Node installed, we can use the terminal to install the Fly binary and log in to our Fly account. In a shell:

npm install -g @fly/fly
fly login

Then we can put the code above in a file named index.js, setup a Fly application and deploy! (you would change redirects-test to your own unique Fly app name). Again in a shell:

fly apps create redirects-test
fly deploy -a redirect-test

Right away we can test it out and see the redirect:

> curl -I http://redirects-test.edgeapp.net/
HTTP/1.1 301 Moved Permanently
Date: Fri, 15 Jun 2018 00:41:00 GMT
Fly-Request-Id: bHmOra288bOTGBD4wh6d6jPU1D
Location: https://redirects-test.edgeapp.net/
Server: Fly.io/0.1.1
Content-Type: text/plain; charset=utf-8

Next up it is quite easy to add another if clause to check for a canonical hostname and redirect if needed:

const canonicalHostname = 'example.com'
if (url.hostname !== canonicalHostname) {
redirectNeeded = true
url.hostname = canonicalHostname
}

And if we test again we can see both the protocol and hostname get updated in the redirect:

> curl -I http://redirects-test.edgeapp.net/
HTTP/1.1 301 Moved Permanently
Date: Fri, 15 Jun 2018 00:43:26 GMT
Fly-Request-Id: bHmOrXMzWz0FSdPPAm4AmJzT04
Location: https://example.com/
Server: Fly.io/0.1.1
Content-Type: text/plain; charset=utf-8

From their to get to production we just need to CNAME our domain’s DNS to our Fly app and let them know which hostnames to route to our app (again swap www.example.com below with your domain and use your unique Fly app name). Back in a shell:

fly hostnames add www.example.com -a redirects-test

Even better, I’ve packed up some common redirection logic for quick integration, check it out on Github:


There is a lot more you can do with Fly edge apps so check out their docs to learn more!