My personal project -- Coffee Chats- - is a platform that allows anyone to create a beautiful, personalized one-page website that syncs with your Google Calendar for easy chat scheduling all in one simple app and without writing any code.
Unlike Calendly or other scheduling apps, one of the biggest benefits of Coffee Chats is that you can have your site hosted on your own, personalized link(e.g. chris.trycoffeechats.com) or you can connect your site to a custom domain like https://www.compoundchoice.co, which is my site for Coffee Chat's parent company and is also powered by Coffee Chats.
In short, Coffee Chats takes the calendaring features in apps like Calendly but combines it with hosting and e-commerce features found in apps like Carrd, Shopify, and Squarespace and combines it all in one app.
But, when I was figuring out how to create an app that supports SSL’d wildcard and custom domains on Heroku, I struggled to find documentation. Specifically, I needed a way to:
After some trial and error, I figured out a way to do this, and so below you’ll find my step-by-step guide to set this up so you can avoid some of the mistakes that I made.
ACM is one of the best things about Heroku and it's what you'll need to use in order to keep your project costs low. If you're not familiar, Automated Certificate Management (ACM) allows Heroku to "automatically manage TLS certificates for apps with Hobby and Professional dynos on the Common Runtime, and for apps in Private Spaces that enable the feature."
In plain English, it means Heroku will manage SSL certs for users who connect their custom domains to your app for free!
Likely, you'll already have this set up as your primary app but you want to confirm your app looks something like the below and you can see that your certificates are automatically managed:
If you do not have ACM set up, click on "Configure SSL" and then select Automatic Certificate Management (ACM) like below:
Cool! But, in order to support wildcard domains (e.g *.trycoffeechats.com), you'll need to use the Manual Certificate option from Heroku, which immediately conflicts with ACM. So what's the solution?
You'll need to create a second app that uses the Manual Certificate option and upload your own wildcard SSL cert for your wildcard customers and have that second app use the same database as your first app.
Yeah, confusing! But, let's step through this one at a time.
To make it easy for us to manage our two apps, we'll want to first create an app pipeline in Heroku.
Go to your app and on the top right, click on "More" then on "Add to pipeline"
If you have a pipeline already created, use that, but assuming this is a brand new thing you're doing, you'll want to click "Create new pipeline"
Then choose your environment preference. Though, assuming you're doing this for your live app, you'll want to choose "Production"
Now that you have your pipeline created, you'll want to make your new app that will just be for wildcard users. To do this, click on "Add app" next to Production (assuming you chose Production on the step above)
Pick your app name and then create your new app. I recommend adding something that signifies this is for your wildcard users.
Now that we have our second app created, go through the normal process of pushing a copy of your app to this new app just for wildcard domains. If you don't know how to deploy code to Heroku, I suggest reading more here.
So, you now have two apps: one for custom domains and one for wildcard domains, but you need them to connect to the same database in order for this to work.
To do that, Heroku has some very convenient commands. So, first, you'll run:
heroku addons:attach my-originating-app::DATABASE --app wildcard-app
Attaching postgresql-addon-name to sushi... done
Setting HEROKU_POSTGRESQL_BRONZE vars and restarting sushi... done, v11
The attached database's URL is assigned to a config var with the name format HEROKU_POSTGRESQL_[COLOR]_URL. In the above example, the config var's name is HEROKU_POSTGRESQL_BRONZE_URL, and "wildcard-app" is the name of the app we're connecting to the original database from your first app (aka my-originating-app).
For the app that just connected to your master database, you will then want to promote this new connected database as the primary one using the command:
heroku pg:promote HEROKU_POSTGRESQL_BRONZE_URL --app wildcard-app
In the above example, HEROKU_POSTGRESQL_BRONZE_URL is the config var for the master database in our connected app and "wildcard-app" is the wildcard app that we just connected to the master database.
So, we now have:
But! One thing that's missing for #2 is that we actually need to upload an SSL cert in order for us to get https URLs for our wildcard users.
There are a lot of ways to do this, but I'll link to the official Heroku documentation to get a wildcard SSL.
Once you have your wildcard certificate, you will upload this to your Heroku app by going into Settings for your wildcard app in Heroku and clicking "Add Certificate"
Then, upload your new wildcard certificate:
Hooray! You now have two apps that both have SSL certificates and we now just need to make some updates to our app to create the user's custom domain on the correct Heroku app, store that information in our database, and then render the correct website content when a visitor navigates to the custom domain.
Now that we have our two apps working and both support SSL'd domains -- the custom domain one managed by Heroku's ACM and the wildcard domain managed by our manual SSL certificate - -we need to write a function that talks to and creates a custom domain site when a user wants to connect their own custom domain.
To do this, we'll have a Rails form that allows a user to enter their preferred custom domain. If you need a tutorial on how to do that, check one out here.
Once a user submits the form (see the Coffee Chats example above), we'll create the custom domain on the custom domain app, Heroku will give us the CNAME a user will need to add to their DNS registrar, we'll store that CNAME and then render it to the user.
All this can be done with the below function:
def create
# create an instance of Heroku's PlatformAPI
heroku = PlatformAPI.connect_oauth(< your heroku_oauth_token >)
# set the user's custom domain value from the form into the variable @custom_domain
@custom_domain = params[:account]["custom_domain"]
# Create a custom domain using the user's provided value on the Heroku app for custom domains
heroku.domain.create(< your custom domain app from heroku >, body = {
"hostname": @custom_domain
})
# Once created, retrieve the custom domain info for the user's custom domain
@domain = heroku.domain.info(< your custom domain app from heroku >, @custom_domain)
# Store the CNAME value for this custom domain from Heroku in your database
@account.update(:heroku_dns_target => @domain["cname"])
# redirect back to the previous form once completed
redirect_back(fallback_location: root_path, notice: 'Custom domain saved.')
end
We are almost done! The last thing we need to do is update our Rails routes so it knows when to pull a website using the subdomain value (e.g. chris.trycoffeechats.com), the custom domain value (e.g. www.compoundchoice.co, or route to the index page of our app (e.g. www.trycoffeechats.com)
To do this, we add the below function to config/routes.rb:
get '/', to: 'subdomain#show', constraints: lambda { |r| !r.host.include? ("trycoffeechats") }
constraints( lambda { |r| r.subdomains.first != "www" } ) do.
get '/', to: 'subdomain#show'
end
root 'pages#index'
There are two lines of magic happening here:
But, now we're sending all these users to subdomain#show
but what does that actually look like?
class SubdomainController < ApplicationController
def show
begin
if request.host.include? "trycoffeechats"
@account = Account.where(:username => request.subdomain).first
else
@account = Account.where(:custom_domain => request.host).first
end
if !@account
return redirect_to root_url(subdomain: "www"), alert: "This site does not exist."
end
rescue => e
puts "error"
puts e
return redirect_to root_url(subdomain: "www", alert: "This user does not exist"
end
end
end
So what is going on above:
If you made it this far, you should now have an app that supports both wildcard and custom domains while providing an SSL option for BOTH domain types. Because of the success of other no-code products like Squarespace, Shopify, and others, this ability to support wildcard and custom domains is now an expected feature so, if you were like me and struggling to figure out a way to do this, I hope this tutorial helps you out.
And, for those of you looking to try this out in production or if you’re just someone with expertise and looking to host chats (free or paid) with guests, visit Coffee Chats and try it out for free!
Chris is the founder of Coffee Chats: a platform that allows anyone to create a beautiful, personalized one-page website that syncs with your Google Calendar for easy chat scheduling all in one simple app. Coffee Chats is perfect for coaches, mentors, influencers, and experts to host free and/or paid chats with their audience. You can book time with Chris using his Coffee Chats site or follow him on Twitter.