maček
maček

Reputation: 77778

How do I redirect without www using Rails 3 / Rack?

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 Ruby on 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 application 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 redirected. All other requests should be ignored.

Upvotes: 36

Views: 13269

Answers (9)

Oliver Morgan
Oliver Morgan

Reputation: 610

In Ruby on 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

Upvotes: 51

Vikrant Chaudhary
Vikrant Chaudhary

Reputation: 11289

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

Upvotes: 6

Jessie Dedecker
Jessie Dedecker

Reputation: 6749

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

Upvotes: 3

Graham Ashton
Graham Ashton

Reputation: 613

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).

Upvotes: 2

scarver2
scarver2

Reputation: 7995

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.

Upvotes: 3

Will Koehler
Will Koehler

Reputation: 1766

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\./ }

Upvotes: 7

Duke
Duke

Reputation: 7444

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

Upvotes: 12

Sean Schofield
Sean Schofield

Reputation: 597

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

Upvotes: 13

Ilya Sabanin
Ilya Sabanin

Reputation: 786

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.

Upvotes: 7

Related Questions