22

My app used to run on foo.tld but now it runs on bar.tld. Requests will still come in for foo.tld, I want to redirect them to bar.tld.

How can I do this in rails routes?

John Bachir
  • 21,401
  • 22
  • 137
  • 203
  • Have you considered DNS forwarding? – Anil Jun 06 '12 at 16:55
  • you can do it in application_controller: if request.host == 'some.site' redirect_to url_for(host: 'site', subdomain: 'another'), status: :moved_permanently end – mmike Oct 20 '16 at 08:30

6 Answers6

43

This works in Rails 3.2.3

constraints(:host => /foo.tld/) do
  match "/(*path)" => redirect {|params, req| "http://bar.tld/#{params[:path]}"}
end

This works in Rails 4.0

constraints(:host => /foo.tld/) do
  match "/(*path)" => redirect {|params, req| "http://bar.tld/#{params[:path]}"},  via: [:get, :post]
end
Professor Todd
  • 494
  • 2
  • 10
Lukas Eklund
  • 5,773
  • 30
  • 32
  • For me this worked everywhere except the home page. The solution is good enough for me. Thank you very much. – Professor Todd Oct 12 '12 at 20:33
  • 1
    The splat rout by itself matches a nonempty string, so root URL won't be matched. Just add the parentheses: `match "/(*path)"...` – janko-m May 15 '13 at 16:25
  • 1
    Rails 4: add `via: [:get, :post]` to the `match` options – meatrobot Nov 04 '13 at 23:48
  • 8
    Use // instead of http:// to ensure the redirect is protocol independent and works for http as well as for https. – Van der Hoorn Feb 27 '14 at 10:01
  • Curious as to why not use: "via: [:get, :post, :put, :patch, :delete]" – justingordon Mar 12 '15 at 00:00
  • @justingordon [This comment](http://stackoverflow.com/questions/4372896/rails-ensuring-www-is-in-the-url#comment4763365_4373733) on a similar question suggests that `GET` and `HEAD` requests should redirect (301 response) while any other methods (`PUT`, `PATCH`, `DELETE`, and `POST`) should technically be blocked (400 or 404 response). I tend to agree with this view since these methods should only be called from within the correct (redirected) site while `GET` requests will most likely come from old links etc. – zelanix Jun 06 '15 at 19:09
  • @VanderHoorn I have a redirection loop with => redirect {|params, req| "http://bar.tld/#{params[:path]}"} and a non secure alert with => redirect {|params, req| "//bar.tld/#{params[:path]}"}, what should i do ? – jmcastel Feb 13 '16 at 08:54
  • I think you want to escape the dot in the domain, i.e. `/foo\.tld/`. Also might want to match end of string, i.e. `/foo\.tld$/` so as not to redirect requests to http://foo.tld.bar.tld/path. – Felix Livni Jul 16 '17 at 00:09
5

This does the job of the other answer. Though in addition, it preserves query strings as well. (Rails 4):

# http://foo.tld?x=y redirects to http://bar.tld?x=y
constraints(:host => /foo.tld/) do
  match '/(*path)' => redirect { |params, req|
    query_params = req.params.except(:path)
    "http://bar.tld/#{params[:path]}#{query_params.keys.any? ? "?" + query_params.to_query : ""}"
  }, via: [:get, :post]
end

Note: If you're dealing with full domains instead of just subdomains, use :domain instead of :host.

Amin Ariana
  • 3,937
  • 1
  • 31
  • 21
  • 2
    Actually `"http://bar.tld#{req.fullpath}"` because `req.fullpath` already contains the leading forward slash. – zelanix Jun 04 '15 at 21:53
3

The following solution redirects multiple domains on GET and HEAD requests while returning http 400 on all other requests (as per this comment in a similar question).

/lib/constraints/domain_redirect_constraint.rb:

module Constraints
  class DomainRedirectConstraint
    def matches?(request)
      request_host = request.host.downcase
      return request_host == "foo.tld1" || \
             request_host == "foo.tld2" || \
             request_host == "foo.tld3"
    end
  end
end

/config/routes.rb:

require 'constraints/domain_redirect_constraint'

Rails.application.routes.draw do
  match "/(*path)", to: redirect {|p, req| "//bar.tld#{req.fullpath}"}, via: [:get, :head], constraints: Constraints::DomainRedirectConstraint.new
  match "/(*path)", to: proc { [400, {}, ['']] }, via: :all, constraints: Constraints::DomainRedirectConstraint.new

  ...
end

For some reason constraints Constraints::DomainRedirectConstraint.new do didn't work for me on heroku but constraints: Constraints::DomainRedirectConstraint.new worked fine.

Community
  • 1
  • 1
zelanix
  • 2,931
  • 1
  • 21
  • 34
2

Bit more modern approach:

constraints(host: 'www.mydomain.com') do
    get '/:param' => redirect('https://www.mynewurl.com/:param')
end
DonMB
  • 2,084
  • 3
  • 19
  • 43
  • Good answer, but (in Rails 5, at least) the param needs to be included in `%{}`. This would give you: `get '/:param', to: redirect('https://www.mynewurl.com/%{param}')` – James Hibbard Dec 16 '19 at 12:27
2

similar to other answers, this one worked for me:

# config/routes.rb
constraints(host: "foo.com", format: "html") do
  get ":any", to: redirect(host: "bar.com", path: "/%{any}"), any: /.*/
end
schpet
  • 6,182
  • 5
  • 24
  • 35
1
constraints(host: /subdomain\.domain\.com/) do
  match '/(*path)' => redirect { |params, req|
    "https://www.example.com#{req.fullpath}"
  }, via: [:get, :head]
end

I use this when using custom domains on Heroku and I want to redirect from the myapp.herokuapp.com -> www.example.com.