Skip to content

Instantly share code, notes, and snippets.

@bsodmike
Created November 16, 2011 06:25
Show Gist options
  • Save bsodmike/1369419 to your computer and use it in GitHub Desktop.
Save bsodmike/1369419 to your computer and use it in GitHub Desktop.
Subdomain Routing with Rails 3.1

Implement Routing for Subdomains

Rails 3.0 introduced support for routing constrained by subdomains.

A subdomain can be specified explicitly, like this:

match '/' => 'home#index', :constraints => { :subdomain => 'www' } 

Or a set of subdomains can be matched using a regular expression:

match '/' => 'profiles#show', :constraints => { :subdomain => /.+/ } 

Finally, for greatest flexibility, router constraints can also take objects, allowing custom code.

Create a class like this:

lib/subdomain.rb

class Subdomain
  def self.matches?(request)
    case request.subdomain
    when 'www', '', nil
      false
    else
      true
    end
  end
end

This class allows use of a route when a subdomain is present in the request object. If the subdomain is “www,” the class will respond as if a subdomain is not present.

Make sure the class is autoloaded when the application starts. You can require 'subdomain' at the top of the config/routes.rb file. Or you can modify the file config/application.rb (recommended):

# config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += %W(#{config.root}/lib)

Use this class when you create routes in the file config/routes.rb:

devise_for :users
resources :users, :only => :show
constraints(Subdomain) do
  match '/' => 'profiles#show'
end
root :to => "home#index"

A match from a “/” URL (such as http://myname.myapp.com) will route to the show action of the Profiles controller only when a subdomain is present. If a subdomain is not present (or is “www”), a route with less priority will be applied (in this case, a route to the index action of the Home controller).

Be sure to comment out (or remove) the route that was added by the Rails generator when we created the controller:

#get "profiles/show"

Home Page

The rails3-mongoid-devise example app provides a home page that lists all registered users. We’ll modify the home page to add a link to each user’s profile page, using a URL with a custom subdomain.

app/views/home/index.html.haml

%h4 Home
- @users.each do |user|
  %br/ 
  User: #{link_to user.name, user}
  Profile: #{link_to root_url(:subdomain => user.name), root_url(:subdomain => user.name)}

URL Helpers With Subdomains

Applications that do not use subdomains use routing helpers to generate links that either include the site’s hostname (for example, users_url generates http://mysite.com/users) or links that only contain a relative path (for example, users_path generates /users). To provide navigation between sites hosted on the subdomains and the main site, you must use URL helpers (“users_url”) not path helpers (“users_path”) because path helpers do not include a hostname. Rails 3.1 provides a way to include a subdomain as part of the hostname when generating links.

You can specify a hostname when creating a link, with the syntax:

root_url(:subdomain => @subdomain)

If you need a link to the main site (a URL without a subdomain), you can force the URL helper to drop the subdomain:

root_url(:host => request.domain)

Is there a better way to do this? Open an issue if you have a suggestion.

Test the Application With Subdomains

If you launch the application, it will be running at http://localhost:3000/ or http://0.0.0.0:3000/. However, unless you’ve made some configuration changes to your computer, you won’t be able to resolve an address that uses a subdomain, such as http://foo.localhost:3000/.

Some Options

There are several complex solutions to this problem. You could set up your own domain name server on your localhost and create an A entry to catch all subdomains. You could modify your /etc/hosts file (but it won’t accommodate dynamically created subdomains). You can create a proxy auto-config file and set it up as the proxy in your web browser preferences.

Use lvh.me

There’s a far simpler solution that does not require reconfiguring your computer or web browser preferences. The developer Levi Cook registered a domain, lvh.me (short for: local virtual host me), that resolves to the localhost IP address 127.0.0.1 and supports wildcards (accommodating dynamically created subdomains).

@MichaelCPell
Copy link

Rails 4 broke this for me. I am going to get to work...

@tonatiuh
Copy link

For emulating subdomains it's possible also to use pow, once the application folder is linked with pow, then the app will respond either only with the domain (http://domain.dev) or with a subdomain and the domain (http://subdomain.domain.dev)

@donmb1
Copy link

donmb1 commented Aug 20, 2015

Well done.
Hint: in rails 4 "match" should get replaced with "get". Rest is superb and solved a big problem for me. Thank you!

@cesc1989
Copy link

cesc1989 commented Jun 9, 2016

Pretty cool. Be forking this gist. Thanks a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment