Serving a Heroku app with a root domain over SSL using Cloudflare

In other words, hosting myapp.herokuapp.com at the naked URL mydomain.com instead of www.mydomain.com

Sam Becker

This is somewhat of a companion to my last dev ops post, Getting Cloudflare, CloudFront + S3 to cooperate over (strict) SSL.

Assumptions

The following instructions assume you have an app hosted on Heroku (myapp.herokuapp.com) and a domain hosted on Cloudflare (mydomain.com). This domain can be held by any registrar (GoDaddy, Namecheap, Route 53, etc.) as long as the name servers point to Cloudflare.

The SSL configuration outlined below requires a paid Heroku app at the hobby tier or higher.

Steps

  1. Go to Heroku > myapp > Settings > Domain and certificates, click Add domain, and enter your root URL (mydomain.com without the www).
  2. Copy the text beneath DNS Target (something like word-thing-2a3erwe04f98g2h34.herokudns.com).
  3. Go to the Cloudflare > mydomain.com > DNS and add two CNAME records:
    (1) Name: mydomain.com Value: [Heroku DNS Target from Step 2]
    (2) Name: www Value: [Heroku DNS Target from Step 2]
  4. The first record should show the following warning (which means you’re on the right track):
    CNAME Flattening will be applied to this record since root (i.e. apex) CNAME records are limited by the DNS specification.
  5. Go to Cloudflare > mydomain.com > Crypto and set SSL to Full (strict).
  6. Scroll down to Origin Certificates and click Create Certificate. Click next with the default settings. Copy the Origin Certificate and Private key. You will need to paste both of these into your Heroku app settings in order for traffic between Heroku and Cloudflare to remain encrypted. This step is a key requirement of strict SSL.
  7. Set Always Use HTTPS to On.
  8. Return to Heroku > myapp > Settings > Domain and certificates and click Configure SSL. Paste in both parts of the Cloudflare certificate generated in Step 6.

Optional steps

  1. Forward all www traffic to your root URL by going to Cloudflare > mydomain.com > Page Rules and clicking Create Page Rule with the following values:
    If the URL matches www.mydomain.com/*
    Then the settings are Forwarding URL, 301-Permanent Redirect,
    https://mydomain.com/$1
    The * and $1 symbols preserve URL arguments when dropping the www.
  2. For full SSL protection, stop serving your Heroku app from its original URL (myapp.herokuapp.com). This must be accomplished from within the logic of your own app. In the case of Rails add the following to application_controller.rb:
class ApplicationController < ActionController::Base
before_action :verify_domain, if: -> { Rails.env.production? }
private
def verify_domain
unless request.host == "mydomain.com"
redirect_to “https://mydomain.com”\
“#{request.original_fullpath}”,
status: :moved_permanently
end
end
end

Feedback welcome

Would love to hear what worked and what didn’t. Happy to update this article as they services change and evolve.

Sam Becker

Written by

Designer and developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade