One of the most common patterns in Rails apps is having a feature that allows your users to have their own subdomain. We were recently working on a Rails 3 application that includes this type of functionality and figured we’d post a few of our tips for making this type of setup work.
Defining the Problem
For our application, we wanted to have subdomains for each user, but we also have static pages for the main site. Those static pages include things like the “about us”, “contact” and “help” pages. It’s reasonable to expect that a user’s website could look something like “mysite.rootdomain.com/about” and the main site could also have a page like “rootdomain.com/about“. We wanted to make sure that Rails can distinguish incoming requests for one “about” page or the other.
Setting up Routes for Subdomains in Rails 3
Rails 3 makes it pretty easy to configure your routes to handle this type of setup, but to do this, we had to use “advanced constraints” in our routes. Basically, you can create a class in your /lib directory that responds to the matches? method. In that method, you can specify what conditions constitute a “match”. Then, in your routes file, you can use that class to define routes that only apply when those conditions are true.
For example, we needed to check two possible cases – (1) the request came from a subdomain or (2) the request came from the root domain.
We created a file called /lib/domains.rb, which includes the following:
# /lib/domains.rb
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != "www" && request.subdomain != ""
end
end
class RootDomain
@subdomains = ["www"]
def self.matches?(request)
@subdomains.include?(request.subdomain) || request.subdomain.blank?
end
end
Here, we are saying that a “subdomain” is matched when there’s a subdomain present in the request AND when that subdomain is not “www” or “”. And the “root domain” is matched when the subdomain is either “www” or “”.
With that taken care of, we can move to the routes.rb file and setup our routes for each of these cases, as follows:
# config/routes.rb
# a bunch of other routes...
# requiring the /lib/domains.rb file we created
require 'domains'
constraints(Subdomain) do
match '/' => 'websites#show'
end
constraints(RootDomain) do
match '/contact_us', :to => 'static_pages#contact'
match '/about', :to => 'static_pages#about'
match '/help', :to => 'static_pages#help'
match '/news', :to => 'static_pages#news'
match '/admin', :to => 'admin#index'
end
Here, you can see a block using the Subdomain class that’s telling Rails that if the incoming request matches what we defined as a “subdomain”, then route to the websites#show action. From that point, we can leave it to the websites controller to determine which user’s website we need to show (based on the subdomain, which is unique) and then off to the view to display the correct user website. Not bad so far. But what about requests for the root domain?
Requests for the Root Domain and Avoiding Conflicts with Subdomain Requests
We want to account for requests that match what we defined as a “domain”, and we want to do this separately so that a subdomain and the root domain can have matching path – for example, in both cases, you could be requesting an /about page.
So above, in our routes.rb file, we’re also telling Rails that if an incoming request matches is a “domain”, the use the routes in the associated block to route those requests.
Conclusion
So those are the basics of what we did. We now have user websites that can be accessed using their unique subdomains. We can also still get to all our pages on the main site and don’t have to worry about conflicts between pages with matching names.
For full details, see the Rails 3 Guide covering Rails Routing from the Outside In or the Railscast on Subdomains that covered this.


