prettymuchbryce
prettymuchbryce

Reputation: 131

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

Upvotes: 5

Views: 1620

Answers (3)

Tadas T
Tadas T

Reputation: 2637

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

Upvotes: 2

hurikhan77
hurikhan77

Reputation: 5931

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

Upvotes: 5

cih
cih

Reputation: 1980

Not sure if this is the best solution but you could regex the request.referrer and pull out anything after .com and append it to the APP_DOMAIN

Or I guess you could just take out everything before the first . in request.env['HTTP_HOST'] add replace with http://www. assuming you don't plan on using subdomains.

Upvotes: 0

Related Questions