chell
chell

Reputation: 7866

How to use gem rack-rewrite to 301 redirect for full URL from naked domain to www domain

I have a Rails 2.3.2 Application hosted on Heroku.

The naked domain and the www subdomain were added years ago and they both have some authority for search engines.

However the problem is that search engines are seeing the content as duplicate so I want to do 301 redirect from the naked domain to the www subdomain.

I have asked Zerigo DNS if I can do this via their platform and they said no, that I have to do it on Heroku's side. Heroku does not allow access to the server so I need some Rack middleware to perform this.

According to this answer here the gem rack-rewrite can do this.

However I really am a NOOB about this and I don't know how to set this up so that any url from the naked domain will be 301 redirected to the www subdomain.

Example:

example.com/products/12 => www.example.com/products/12

I am currently doing the wrong things in the application controller as follows:

 before_filter :ensure_domain

protected 

  def ensure_domain
      app_domain = "www.example.com"
      naked_domain = "example.com"
      page_path = request.fullpath
    if request.env['HTTP_HOST'] == naked_domain  && RAILS_ENV == 'production'
      #HTTP 301 is a permanent redirect
        redirect_to "http://#{app_domain}#{page_path}", :status => 301
    end
  end

This works but I know I should be doing this in Rack Middleware.

Can someone show me how to set this up with the rack-rewrite gem?

I have added the following to my application_controller.rb file:

config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
 r301 %r{.*}, 'http://www.example.com$&', :if => Proc.new {|rack_env|
  rack_env['SERVER_NAME'] != 'www.example.com'
}
end

Which produces the following error:

NameError (undefined local variable or method `config' for ApplicationController:Class):

Any ideas on how I can get this setup working?

Upvotes: 4

Views: 2218

Answers (4)

chell
chell

Reputation: 7866

I turns out that I had two problems when using this gem.

The first was that I was putting the redirect code in the Application_Controller instead of the config.ru file.

The second was that the code I was using was not correct.

Here is the code I put in the config.ru file:

if RAILS_ENV =='production'
    use Rack::Rewrite do
        r301 %r{.*}, 'http://www.example.com$&', :if => Proc.new {|rack_env|
        rack_env['SERVER_NAME'] != 'www.example.com'
        }
    end
end

Upvotes: 0

Ilya Lavrov
Ilya Lavrov

Reputation: 2860

Also you can use rack-host-redirect gem. Add to Gemfile:

gem 'rack-host-redirect'

in config/environments/production.rb:

config.middleware.use Rack::HostRedirect, {
  'yourdomain.com' => 'www.yourdomain.com'
}

With this configuration, all requests to yourdomain.com will be 301 redirected to www.yourdomain.com.

Upvotes: 2

Ilya Vassilevsky
Ilya Vassilevsky

Reputation: 1001

According to the gem's README, you need to add the following to your application's config/application.rb:

config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
  moved_permanently /.*/, 'http://www.example.com$&', if: ->(env){env['SERVER_NAME'] != 'www.example.com'}
end

I have modernized the example code and tested it on a Rails 3 application.

The arguments to the moved_permanently method are:

  1. /.*/ is a regular expression matching any request path and url parameters, therefore, any page on the site.
  2. 'http://www.example.com$&' is the new full URL of the page (after the redirect). It has the right domain name and the original request path attached to it — the $& symbols mean "whatever is matched by the first parameter" and it matches everything.
  3. if: ->(env){env['SERVER_NAME'] != 'www.example.com'} is a hash of options for this rewrite rule. In this case it only has one option named if. The option's value is a lambda function: ->(env){env['SERVER_NAME'] != 'www.example.com'}.

The gem will call this lambda for each request. It will pass a hash of all environment variables to it as a parameter. (I called the parameter env but the name can be any.)

The lambda reads from it the domain name that the client used to make a request to the site: env['SERVER_NAME']. It checks if the domain is the right one.

  • It returns true if the domain is incorrect. In this case, and in this case only, will the gem rewrite the URL and return status 301 to the client.
  • It returns false if the domain is the right one. In this case, no action will be taken and the request will "fall through" to the application (to the other middlewares).

Upvotes: 0

The Whiz of Oz
The Whiz of Oz

Reputation: 7043

example.com/products/12 => www.example.com/products/12

In order to tackle this problem with Heroku I had to set up my domain hoster's setting correctly. What you are looking for is called Forwarding. Next, you want to enable the 'keep relative path' option.

So, look for:

  1. Redirect type: 301 permanent
  2. Forward settings: forward only
  3. Keep relative path

This should all be available on your domain registrar's level.

Upvotes: 0

Related Questions