Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails manually redirecting from naked domain

So currently I am manually directing from a naked domain due to restrictions with my hosting provider (Heroku). Everything works just fine. The problem is that if a users visits mydomain.com/route, a redirect will be issued back to www.mydomain.com without the /route. How would I go about re-appending the route, but still redirecting to www. ?

class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :ensure_domain
APP_DOMAIN = 'www.domain.com'
def index
end
def ensure_domain
    if Rails.env.production?
        if request.env['HTTP_HOST'] != APP_DOMAIN
            redirect_to "http://#{APP_DOMAIN}", :status => 301
        end
    end
end
end

EDIT

I removed my code above from my ApplicationController, and opted for using the refraction gem as suggested by hurikhan77, which solved my problem.

Here is refraction_rules.rb I used.

Refraction.configure do |req|
  if req.host == "domain.com"
    req.permanent! :host => "www.domain.com"
  end
end
like image 651
prettymuchbryce Avatar asked Nov 23 '12 23:11

prettymuchbryce


2 Answers

I suggest using the refraction gem for this: http://rubygems.org/gems/refraction

like image 186
hurikhan77 Avatar answered Sep 30 '22 03:09

hurikhan77


Ideally, you would set up rules like that in your web server configuration. Requests would become faster, because they would not even reach the rails stack. There would be no need to add any code to your app either.

However, if you are running in some restricted environment, like heroku, I'd advise adding a rack middleware. (Just for guidelines, can't guarantee if this particular code is bug free)

class Redirector
  SUBDOMAIN = 'www'

  def initialize(app)
    @app = app
  end

  def call(env)
    @env = env
    if redirect?
      redirect
    else
      @app.call(env)
    end
  end

private

  def redirect?
    # do some regex to figure out if you want to redirect
  end

  def redirect
    headers = {
      "location" => redirect_url
    }
    [302, headers, ["You are being redirected..."]] # 302 for temp, 301 for permanent
  end

  def redirect_url
    scheme = @env["rack.url_scheme"]

    if @env['SERVER_PORT'] == '80'
      port = ''
    else
      port = ":#{@env['SERVER_PORT']}"
    end

    path = @env["PATH_INFO"]

    query_string = ""
    if !@env["QUERY_STRING"].empty?
      query_string = "?" + @env["QUERY_STRING"]
    end

    host = "://#{SUBDOMAIN}." + domain # this is where we add the subdomain
    "#{scheme}#{host}#{path}#{query_string}"
  end

  def domain
    # extract domain from request or get it from an environment variable etc.
  end
end

You can also test the whole thing in isolation

describe Redirector do
      include Rack::Test::Methods

      def default_app
        lambda { |env|
          headers = {'Content-Type' => "text/html"}
          headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly"
          [200, headers, ["default body"]]
        }
      end

      def app()
        @app ||= Rack::Lint.new(Redirector.new(default_app))
      end

      it "redirects unsupported subdomains" do
        get "http://example.com/zomg?a=1"
        last_response.status.should eq 301
        last_response.header['location'].should eq "http://www.example.com/zomg?a=1"
      end
     # and so on
end

Then you can add it to production (or any preferred environments) only

# production.rb
# ...
config.middleware.insert_after 'ActionDispatch::Static', 'Redirector'

If you want to test it in development, add the same line to development.rb and add a record to your hosts file (usually /etc/hosts) to treat yoursubdomain.localhost as 127.0.0.1

like image 25
Tadas T Avatar answered Sep 30 '22 03:09

Tadas T