Lessons Learned Building a Rails App With Wildcard Subdomains
I recently launched my first software as a service web app called MicroSweepstakes, which allows you to build a simple sweepstakes website and have it hosted on a subdomain of microsweepstakes.com. This follows the same pattern as other do it yourself site builders like Squarespace, Weebly, WordPress, Wix, etc. There were a few key things I learned in the process.
Some DNS services support wildcard domains
I first bought a domain on DreamHost, but quickly learned that DreamHost did not support wildcard domains. According to the forums, you can activate wildcard domains, but you will have to use their VPS services and then go through the process of contacting DreamHost support. I did initially try to work with support to get the wildcard domains set up and pointed to my Digital Ocean IP, but eventually gave up due to the hassle. I ended up purchasing another domain on DNSimple, and the process was fairly simple. The wildcard domain I need worked right off the bat. The annual expense for DNSimple ended up being $50+, after all of their service fees, WHOIS protection, and domain renewal, as opposed to DreamHosts’ $10.95/year. The lesson here is figure out if you need wildcard subdomains before purchasing your domain. Some providers support it. Some don’t.
Two types of SSL certificates
There are two types of SSL certificates you can purchase through DNSimple/Comodo. A single subdomain SSL certificate which runs for $20/year, and a wildcard subdomain SSL certificate which runs for $100/year. Without thinking it through carefully, I purchased the single subdomain SSL certificate assuming that I would need it only for the checkout page on my web app. By purchasing the single subdomain SSL certificate, I added another layer of complexity to my multiple subdomain app. Because some pages required SSL and some didn’t, I had to work the logic into my Rails app. I figured it would have been easier to just purchase the wildcard SSL certificate to make everything work within the https protocol instead of both http and https. So that is exactly what I did. I purchased the $100 SSL certificate. Then I realized that because my app supported custom domains, which allows users to point their domain to my app, the wildcard SSL certificate for microsweepstakes.com was essentially useless, since it wouldn’t even matter because of custom domains. Essentially, each custom domain would need its own SSL certificate for the entire site to run on https. I decided to go back to using the single subdomain SSL certificate only on the user dashboard, and then disable SSL on any custom domain parts of the app. The lesson learned here is to think more about how I’m going to structure the secure parts of my app before rushing into purchasing SSL certificates.
Working with subdomains locally
There are few strategies for working with subdomains locally. Pow and lvh.me. With Pow, you can run your app on a .dev top level domain locally, for example microsweepstakes.dev. With lvh.me, you can run your subdomain on lvh.me:3000. There a few things to consider. With lvh.me, byebug and/or pry works out of the box. You just run your server and see your log output which stops if there is a byebug/pry breakpoint. The only downside is that it requires an internet connection to work. Pow, on the other hand doesn’t require an internet connection. The downsides are that it is difficult to set up and byebug/pry does not work out of the box. You’ll have to connect to the byebug/pry through the command line, and it made the app slow when connected. I would work with lvh.me so long as I have an internet connection which is 99% of the time.
Rails route constraints
Rails has some useful features for dealing with subdomains. In your routes file, you can use lambdas to add constraints to your routes. Here is an example:
# microsite routes for custom domains get '', to: 'microsite#home', constraints: lambda { |r| !(r.host =~ /microsweepstakes.com|microsweepstakes.dev|lvh.me/) } get ':permalink', to: 'microsite#show', constraints: lambda { |r| !(r.host =~ /microsweepstakes.com|microsweepstakes.dev|lvh.me/) } post 'create_entry', to: 'microsite#create_entry', constraints: lambda { |r| !(r.host =~ /microsweepstakes.com|microsweepstakes.dev|lvh.me/) } # microsite routes for microsite domain get '', to: 'microsite#home', constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' } get ':permalink', to: 'microsite#show', constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' } post 'create_entry', to: 'microsite#create_entry', constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' }
The lambda places a condition for the route by accessing the request object, so you can route requests based on the subdomain and/or host name.
Conclusion
Working with dynamic subdomains and SSL certificates can get extremely complicated with many gotchas. Think carefully about your SSL needs before spending your money. Make sure your DNS provider supports wildcard domains before registering, if you need wildcard subdomains. Use lvh.me to work with subdomains. Use lambdas to place constraints on routes.