Skip to main content

Command Palette

Search for a command to run...

How to Add Dynamic Subdomain in Rails Application

Published
2 min read
How to Add Dynamic Subdomain in Rails Application

For those who want to implement subdomain programmatically within your rails application, you can follow these few steps:

1. Generate Account Model

First, let’s create an Account model as a table to store subdomain names that we will accept in our application.

rails g model Account subdomain:string

From the above generator, it generates a migration file. We can then do rails db:migrate.

2. Add Request-based Constraints dan Modify routes.rb

Create a new file inside the /lib folder and give it the name, say subdomain.rb.

Add this code inside that file:

class Subdomain
  def self.matches?(request)
    request.subdomain.present? && request.subdomain != "www"
  end
end

Then we need to modify a bit our routes.rb by adding constraints to every URLs that we will use under the subdomain. For an example, pages_controller.rb needs to be accessible under a specific subdomain, so we can write our route like this:

Rails.application.routes.draw do
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html

  constraints(Subdomain) do

    get '/', to: 'pages#index'

  end

end

3. Add Authorization to Controller

We will then need to add before_action :require_subdomain since the controllers need to be strictly accessible from the subdomain only. This is achievable by modifying our application_controller.rb:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  helper_method :require_subdomain

  private

  def require_subdomain
    not_found unless subdomain_exist?
  end

  def subdomain_exist?
    account = Account.find_by_subdomain(request.subdomain)
    !!account
  end

  def not_found
    raise ActionController::RoutingError.new('404')
  end
end

In the application_controller.rb above we now have a helper method called require_subdomain, which is reusable to each controller that requires to be authorized by the subdomain.

class PagesController < ApplicationController
  before_action :require_subdomain

  def index
    # controller logic goes here
  end
end

4. Try It Out

Let's use rails console to create a dummy for our Account or subdomain.

$ rails c
Running via Spring preloader in process 15151
Loading development environment (Rails 6.0.0)
2.6.5 :001 > Account.new(subdomain: 'test').save
   (0.4ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
   (0.3ms)  BEGIN
  Account Exists (0.7ms)  SELECT  1 AS one FROM `accounts` WHERE `accounts`.`subdomain` = BINARY 'test' LIMIT 1
  SQL (0.5ms)  INSERT INTO `accounts` (`subdomain`, `created_at`, `updated_at`) VALUES ('test', '2017-07-22 14:58:35', '2017-07-22 14:58:35')
   (35.5ms)  COMMIT
 => true

Open the browser then visit http://test.lvh.me:3000 and also try accessing some random subdomain, for example, http://doesnotexist.lvh.me:3000.

About lvh.me domain, you can check this article out

If the subdomain is registered in Account

download (2).png

Subdomain doesn't exist in Account

download (1).png

Tip:

We can add some extra validation layers in the Account creation so we can preserve some subdomain names so your user cannot claim those (i.e. www, admin, app, etc).

Thank you!