I. Introduction
Hello everyone. When working on a project, we often see 1 domain that contains many other subdomains. Let’s say we have a fashion.com
fashion website and we want to support multiple subdomains like dev.fashion.com
, app.fashion.com
, api.fashion.com
in a Rails application and want to ensure that authentication For all subdomains and no routes are duplicated. The question is: “How to create a Rails app that can support multiple subdomains
“.
In this post, let me find out:
- What is a subdomain?
- Config subdomain in Rails app.
- Set up multiple local subdomains for the development environment.
Let’s go !!!
II. Detail
1. What is a Subdomain?
Subdomain
, also known as subdomains (subdomains). This is a part extracted from the Domain.Subdomain
works separately as a normal website and has the same primary domain. And it is completely separate from another website (so in terms of SEO, it does not enjoy any backlinks from the main domain).
As originally assumed:
fashion.com
: is the main domain (domain)dev.fashion.com
,app.fashion.com
,api.fashion.com
: subdomains
By using the Subdomain name, you create a completely separate website, operating independently without the cost of registering a new domain name or having the hassle of handling domain name redirection.
To learn more about subdomains you can visit this link.
2. Config subdomain in Rails app.
2.1 Handling multiple subdomains
Rails uses the routes.rb
file to handle request
and maps them to specific controller
action
. For example:
1 2 |
get "/fashions", to: "fashions#index" |
In the above example, all endpoints defined in routes.rb
can be applied to all subdomain
. So api.fashion.com/fashions
or app.fashion.com/fashions
also handled according to this route. The problem is that we only want the request from the api
subdomain to be processed in this route and the app
subdomain is only used for APP routes. We will have to add a few rules to our routes so that they will be processed only when a specific rule is met when making a request.
Rails provides a method that constraints
can specify additional rules for a given route.
1 2 |
get "/fashions", to: "fashions#index", constraints: { subdomain: "api" } |
This ensures that if the request comes from api.fashion.com/fashions
, it will be handled by FashionsController#index
. All requests from other subdomains like the app
will not be processed by this route.
1 2 3 4 |
get "/fashions", to: "fashions#index", constraints: { subdomain: "api" } get "/fashions/:id", to: "fashions#show", constraints: { subdomain: "api" } post "/fashions", to: "fashions#create", constraints: { subdomain: "api" } |
We can use bock for constraints
to define multiple routes for a subdomain.
1 2 3 4 5 6 |
constraints subdomain: "api" do get "/fashions", to: "fashions#index" get "/fashions/:id", to: "fashions#show" post "/fashions", to: "fashions#create" end |
To define routes for multiple subdomains, we just need to add block constraints
in the routes.rb
file.
1 2 3 4 5 6 7 8 9 10 11 12 |
constraints subdomain: "api" do ... end constraints subdomain: "app" do ... end constraints subdomain: "dev" do ... end |
Rails provides request constraints
and segment constraints
. Segment constraints
add rules on the request while request constraints
add conditions on the request. Hash key
in a request constraints must be a method on the Request object
returns a string
and the value needs to be the expected value.
1 2 3 4 |
constraints subdomain: "api" do ... end |
In the above case, we are using the subdomain
method on the Request
object and matching it with a string like api, app or dev.
Handling multi-level subdomains
We use app.staging.fashion.com
for the staging environment. If we install the above, we will notice that all requests that are called to the subdomain app
return error 404. If we try to debug in, we will see that the constraint for the subdomain will fail.
1 2 |
request.subdomain #=> app.staging |
We expect it to return the subdomain app
, but instead of returning it as expected, it returns app.staging.
We want to solve this problem without adding enviroment. The subdomain domain parser is managed by config.action_dispatch.tld_length
. The default value of this configuration is 1, basically supporting a level subdomain.
Since we have 2 levels of subdomains, we need to set the value for config.action_dispatch.tld_length
2.
1 2 3 |
# config/application.rb config.action_dispatch.tld_length = Integer(ENV['TLD_LENGTH'] || 1) |
We can set it up using an environment variable so we can use the same code in the staging environment as in the production environment. Now, routing setup will work for app.staging.fashion.com
.
2.2. Session management
Now that the routes have been defined to handle requests coming from multiple subdomains, we need to take care of the authentication for all subdomains. We can do this in 2 ways:
- Use one session to use on all subdomains.
- Separate sessions are used on separate subdomains.
Rails uses cookies to store session keys. When the user logs in, the session information is stored in the session store and the session key is stored as a cookie in the browser. Therefore, the next time the user visits the site, the same session cookie is sent from the browser to the server and the server determines whether the user is logged in based on whether the session exists for the next time.
The default configuration for sessions in Rails:
1 2 |
Rails.application.config.session_store :cookie_store, key: "_fashions_session" |
The _fashions_session
key will be used as the name of the session cookie and its value is the session id.
Cookies primer
By default, cookies are placed on request’s domain by the browser. So if we get to our app from app.fashions.com
, the session cookie will be set with app.fashions.com
. Each subdomain will set its own session cookie, so the user session will not be shared on subdomains by default.
Share sessions between different subdomains
If we want to share the user session between subdomains, we will place the session cookie on domain fashions.com
so that all subdomains can access it. This can be done by moving the domain
option to the session store settings.
1 2 |
Rails.application.config.session_store :cookie_store, key: "_fashions_session", domain: :all |
By turning the domain
to :all
, we are telling Rails to set the session cookie on the top level of the application like fashions.com
instead of on the request server, which can include individual subdomains. When we do so, the session may be shared between different subdomains.
We can also turn the list of domains into custom domains into one array to support multiple domains
There is one more option that needs to be configured to set cookies correctly for all subdomains. Is the tld_length
option. When using domain: :all
, this option can specify how to parse the domain to interpret the domain’s TLD. In case of app.fashions.com
we should set tld_length
to 2 so Rails interprets the TLD as fashions.com
when setting cookies. So, the final session store configuration for multiple subdomains would look like this:
1 2 3 4 5 |
Rails.application.config.session_store :cookie_store, key: "_fashions_session", domain: :all, tld_length: 2 |
3. Config multiple local subdomains for development environment
There are many ways to set up subdomains locally. The easiest is to edit in file: /etc/hosts
1 2 3 4 |
127.0.0.1 dev.fashions.local 127.0.0.1 app.fashions.local 127.0.0.1 api.fashions.local |
This ensures that subdomains setup will work in the local environment. We can also use tools like pow to manage local subdomains.
III. Conclude
Above is my find out about subdomains and how to configure subdomains in Rails app, hope to help you. Thanks for reading.
Reference links: