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
I suggest using the refraction gem for this: http://rubygems.org/gems/refraction
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With