Redirect non-www requests to www URLs in Ruby on Rails
Ideally you'd do this in your web server (Apache, nginx etc.) configuation so that the request doesn't even touch Rails at all.
Add the following before_filter
to your ApplicationController
:
class ApplicationController < ActionController::Base
before_filter :add_www_subdomain
private
def add_www_subdomain
unless /^www/.match(request.host)
redirect_to("#{request.protocol}x.com#{request.request_uri}",
:status => 301)
end
end
end
If you did want to do the redirect using Apache, you could use this:
RewriteEngine on
RewriteCond %{HTTP_HOST} !^www\.x\.com [NC]
RewriteRule ^(.*)$ http://www.x.com/$1 [R=301,L]
For rails 4, use it -
before_filter :add_www_subdomain
private
def add_www_subdomain
unless /^www/.match(request.host)
redirect_to("#{request.protocol}www.#{request.host_with_port}",status: 301)
end
end
While John's answer is perfectly fine, if you are using Rails >= 2.3 I would suggest to create a new Metal. Rails Metals are more efficient and they offers better performance.
$ ruby script/generate metal NotWwwToWww
Then open the file and paste the following code.
# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
class NotWwwToWww
def self.call(env)
if env["HTTP_HOST"] != 'www.example.org'
[301, {"Content-Type" => "text/html", "Location" => "www.#{env["HTTP_HOST"]}"}, ["Redirecting..."]]
else
[404, {"Content-Type" => "text/html"}, ["Not Found"]]
end
end
end
Of course, you can customize further the Metal.
If you want to use Apache, here's a few configurations.
There is a better Rails 3 way - put this in your routes.rb
file:
constraints(:host => "example.com") do
# Won't match root path without brackets around "*x". (using Rails 3.0.3)
match "(*x)" => redirect { |params, request|
URI.parse(request.url).tap { |x| x.host = "www.example.com" }.to_s
}
end
Update
Here is how to make it domain agnostic:
constraints(subdomain: '') do
match "(*x)" => redirect do |params, request|
URI.parse(request.url).tap { |x| x.host = "www.#{x.host}" }.to_s
end
end