Service providers everywhere on the internet protect their websites with HTTPS. Previously, HTTPS was only used for login forms and pages that transmitted confidential data over the wire (e.g. your credit card information). A lot around this has changed in recent years and encryption is becoming the standard for the entire web. In this post, we will go through the steps we have taken to secure trayn.com which currently runs on Heroku.

At Trayn, our users’ data and how they interact with it has always been protected with HTTPS. With encryption enabled, we can offer security and data integrity features: a lot of new standards build on HTTPS and won’t work on HTTP. In the last 5 years, app.trayn.com, the application, was configured for HTTPS-only and we always kept everything up to date.

Our website, trayn.com, runs on Heroku, which is a very good solution for a small dynamic website. We also use Amazon Web Services as our DNS provider via Route53. So, to have HTTPS running everywhere, we needed to configure a few things and we thought others might need to go through similar steps.

Heroku SSL

Heroku provides free SSL services for all paid dynos and can manage certificates automatically via Let’s Encrypt. You can configure a custom domain and will receive an endpoint that can be configured in DNS:

Running the heroku domains command would look something like this after configuring a domain name.

$ heroku domains
=== [...] Heroku Domain
....

=== [...] Custom Domains
Domain Name    DNS Target
─────────────  ───────────────────────────
www.trayn.com  www.trayn.com.herokudns.com

On the DNS side, we have to create a CNAME record for www.trayn.com and point it to www.trayn.com.herokudns.com. That’s it for the configuration for Heroku.

As you can see, we have not configured a domain name for trayn.com, the naked domain. This is because with Route53 as DNS provider you cannot easily use a CNAME record and point to the endpoint in Heroku.

AWS CloudFront and S3

So, for the naked domain to work too, we will unfortunately have to do a little bit more on the AWS side:

  1. Create an S3 bucket that forwards requests from the naked domain to www (trayn.comwww.trayn.com). See here for more information.
  2. Create a certificate in AWS Certificate Manager (ACM) for trayn.com. You can include www.trayn.com in that certificate too.
  3. Create a new CloudFront distribution with
    • The origin set to the website endpoint of the S3 bucket: <bucket>.s3-website-<region>.amazonaws.com
    • The certificate created in ACM

It takes a while for the CloudFront distribution to be deployed, but as soon as it’s done, you can check with curl whether redirects work, we want trayn.com to point to www.trayn.com com on HTTP and on HTTPS.

$ curl -sI http://trayn.com | grep Location
Location: https://trayn.com/
$ curl -sI https://trayn.com | grep Location
Location: http://www.trayn.com/

Redirects work, but a request to https://trayn.com resulted in a redirect to http://www.trayn.com/ (HTTP instead of HTTPS).

Laravel

The website currently runs on Laravel and we do not automatically redirect from HTTP to HTTPs. However, internally, we want to make sure that requests are considered secure internally, if they come via a secured connection. This parts hurts most, because this took a lot of time from our simple upgrade.

The reverse proxies and load balancers in Heroku’s infrastructure use HTTP for internal requests. This means the request reaching our website will be an HTTP request, even though, externally it is served as HTTPS. However, digging a little deeper in the Heroku documentation, we found out that you can use a dedicated proxy package to trust X-Forwarded-* headers and serve pages correctly.

Summary

As you can put your name and email address for a sign up into a form on our homepage, you can now be sure that it is transferred securely. We hope this small article also helps other to upgrade their infrastructure accordingly.