Question

I understand there are a lot of questions that answer this. I'm familiar with .htaccess and nginx.conf methods, but I do not have access to such traditional configuration methods on heroku.

Simone Carletti gave this answer that leverages Rails 2.x Metals, but I'm using Rails 3 and this isn't compatible. Redirect non-www requests to www urls in Rails

Please note:

I'm not looking for a simple before_filter in my ApplicationController. I'd like to accomplish a rewrite similar to Simone's. I believe this is job for the webserver or middleware like Rack at the very least, so I'd like to leave this bit out of the actual app code.

Goal

redirect                to                  status
----------------------------------------------------
www.foo.com             foo.com             301
www.foo.com/whatever    foo.com/whatever    301

Only hosts matching /^www\./ should be redirect. All other requests should be ignored.

Was it helpful?

Solution

in Rails 4 removing www. from any URL whilst maintaining the pathname can be achieved simply by using:

# config/routes.rb

constraints subdomain: 'www' do
  get ':any', to: redirect(subdomain: nil, path: '/%{any}'), any: /.*/
end

In contrast, adding www. to the beginning of any URL that doesn't already have it can be achieved by:

# config/routes.rb

constraints subdomain: false do
  get ':any', to: redirect(subdomain: 'www', path: '/%{any}'), any: /.*/
end

OTHER TIPS

There's a better approach if you're using Rails 3. Just take advantage of the routing awesomeness.

Foo::Application.routes.draw do
  constraints(:host => /^example.com/) do
    root :to => redirect("http://www.example.com")
    match '/*path', :to => redirect {|params| "http://www.example.com/#{params[:path]}"}
  end
end

I really like using the Rails Router for such things. Previous answers were good, but I wanted something general purpose I can use for any url that starts with "www".

I think this is a good solution:

constraints(:host => /^www\./) do
  match "(*x)" => redirect { |params, request|
    URI.parse(request.url).tap {|url| url.host.sub!('www.', '') }.to_s
  }
end

Take a look at this middleware, it should do precisely what you want:

http://github.com/iSabanin/www_ditcher

Let me know if that worked for you.

A one-line version of Duke's solution. Just add to the top of routes.rb

match '(*any)' => redirect { |p, req| req.url.sub('www.', '') }, :constraints => { :host => /^www\./ }

In Rails 3

#config/routes.rb
Example::Application.routes.draw do
  constraints(:host => "www.example.net") do
    match "(*x)" => redirect { |params, request|
      URI.parse(request.url).tap { |x| x.host = "example.net" }.to_s
    }
  end
  # .... 
  # .. more routes ..
  # ....
end

If you want to redirect from the top-level domain (TLD) to the www subdomain, use this code:

constraints :subdomain => '' do
  match '(*any)' => redirect { |p, req| req.url.sub('//', '//www.') }
end

Note: This code the use of sub, not gsub, because sub replaces the first occurrence of the double-slashes where gsub would replace all double-slashes.

For Rails 4 the above solutions have to be appended with the Verb construction e.g. via: [:get, :post]. Duke's solution becomes:

  constraints(:host => /^www\./) do
    match "(*x)" => redirect { |params, request|
      URI.parse(request.url).tap {|url| url.host.sub!('www.', '') }.to_s
    }, via: [:get, :post]
  end

Nothing wrong with the approaches above, but there are also a couple of gems that provide Rack middleware to do this.

I like the way that they keep this behaviour separate from the app itself, but it's not a particularly strong argument either way. I also use middleware to do this when working with Sinatra, so prefer to use a technique that I can use on apps built from Rails and/or Sinatra (I often run Nesta embedded in Rails).

Anyway, here they are:

The first is simpler (and the one I've been using) while the second offers a couple more features (that I'm yet to need, but appreciate).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top