5

I have my app hosted on Heroku, and have a cert for www.mysite.com

I'm trying to solve for

  • Ensuring www is in the URL, and that the URL is HTTPS

Here's what I have so far:

class ApplicationController < ActionController::Base
before_filter :check_uri

  def check_uri
    redirect_to request.protocol + "www." + request.host_with_port + request.request_uri if !/^www/.match(request.host) if Rails.env == 'production'
  end

But this doesn't seem to being working. Any suggestions or maybe different approaches to solve for ensuring HTTPs and www. is in the URL?

Thanks

John Topley
  • 107,187
  • 45
  • 188
  • 235
AnApprentice
  • 97,752
  • 174
  • 583
  • 971
  • Hi all, I posted a +500 bounty, I think Justice is headed in the right track with the suggestion below but I haven't been able to get it to work. Perhaps a pair of fresh eyes can help solve this for good? Thank you – AnApprentice Dec 09 '10 at 06:15
  • Can you clarify how it is not working? Is nothing happening at all? Are you getting redirects when you shouldn't be? Additionally, do you want all access to your app to be done via https or just for specific urls? Sounds like you want the former, but just making sure... – super_seabass Dec 14 '10 at 22:20

6 Answers6

11

For the SSL, use rack-ssl.

# config/environments/production.rb
MyApp::Application.configure do
  require 'rack/ssl'
  config.middleware.use Rack::SSL
  # the rest of the production config....
end

For the WWW, create a Rack middleware of your own.

# lib/rack/www.rb
class Rack::Www
  def initialize(app)
    @app = app
  end
  def call(env)
    if env['SERVER_NAME'] =~ /^www\./
      @app.call(env)
    else
      [ 307, { 'Location' => 'https://www.my-domain-name.com/' }, '' ]
    end
  end
end

# config/environments/production.rb
MyApp::Application.configure do
  config.middleware.use Rack::Www
  # the rest of the production config....
end

To test this in the browser, you can edit your /etc/hosts file on your local development computer

# /etc/hosts
# ...
127.0.0.1 my-domain-name.com
127.0.0.1 www.my-domain-name.com

run the application in production mode on your local development computer

$ RAILS_ENV=production rails s -p 80

and browse to http://my-domain-name.com/ and see what happens.

For the duration of the test, you may want to comment out the line redirecting you to the HTTPS site.

There may also be ways to test this with the standard unit-testing and integration-testing tools that many Rails projects use, such as Test::Unit and RSpec.

yfeldblum
  • 63,188
  • 11
  • 126
  • 168
  • @Justice you're a lifesaver! I have rack-ssl working fantastically. Couple questions. Is Www purposely capitalized that way? Can I use all caps? Also, is there any way to redirect to the https.... and maintain the path or is that to much risk? thank you!!! – AnApprentice Dec 07 '10 at 05:50
  • 1
    The `Www` is my preference. Feel free to use your own style. – yfeldblum Dec 07 '10 at 06:02
  • You can certainly make this code more complete, keeping the scheme, path-prefix, path, and query-string intact. You can easily base it on the rack-ssl code https://github.com/josh/rack-ssl/blob/master/lib/rack/ssl.rb#L47. – yfeldblum Dec 07 '10 at 06:05
  • 1
    One more item - for `GET` and `HEAD` requests, you should redirect. But for any other methods (`PUT`, `DELETE`, and `POST`), technically, you should block the request (issue a `400` or `404`). – yfeldblum Dec 07 '10 at 06:06
  • Just tried it out on Heroku, error'd with "uninitialized constant Rack::Www (NameError)" – AnApprentice Dec 07 '10 at 06:27
  • It doesn't seem to be finding the middleware: "/config/environments/production.rb:57: uninitialized constant Rack::Www (NameError)" – AnApprentice Dec 07 '10 at 06:40
  • I was able to get it up and running but it had no effect. What I did was change the production.rb line to: config.middleware.use "Www" then I placed the www.rb file here /lib – AnApprentice Dec 07 '10 at 06:50
  • Any ideas? or suggestions? Were my modifications a problem? – AnApprentice Dec 07 '10 at 06:50
  • 1
    Your modifications should work, though I might have just stuck a `require 'rack/www'` at the top of `config/environments/production.rb`. – yfeldblum Dec 07 '10 at 11:57
  • @Justice, I don't follow. "Struck?" Still haven't been able to get this to work. Any ideas? – AnApprentice Dec 07 '10 at 17:57
  • Trying it out locally, these are the errors "[2010-12-07 10:47:36] ERROR bad Request-Line `UQL??H?S – AnApprentice Dec 07 '10 at 18:48
  • require 'rack/Www' results in: ...activesupport-3.0.0/lib/active_support/dependencies.rb:239:in `require': no such file to load -- rack/Www (LoadError) – AnApprentice Dec 07 '10 at 18:56
  • 1
    Filenames - and therefore, what you pass as the argument to `require` - are case-sensitive. Try `require 'rack/www'`. – yfeldblum Dec 08 '10 at 01:29
  • @justice, hmm, still no luck "no such file to load -- rack/www" – AnApprentice Dec 08 '10 at 06:25
  • ok got that error removed by moving www.rb to lib/rack/www.rb – AnApprentice Dec 08 '10 at 06:36
  • Just finished testing, I also tried Changing to "config.middleware.use Rack::Www" and "class Rack::Www" which error'd, prevented rails for event loading up the app. – AnApprentice Dec 08 '10 at 06:42
  • 1
    Any other ideas or suggestion on how to debug? I'm curious if the 307 or the www.rb is even being called? – AnApprentice Dec 08 '10 at 06:43
  • 1
    Added some info about testing it out in the browser. – yfeldblum Dec 08 '10 at 17:42
  • Trying this out now. Are the hosts backwards? doesn't it go site.com IP not IP site? – AnApprentice Dec 09 '10 at 18:24
  • on my local machine, if I try booting to production with port 80 it errors: "Permission denied - bind(2) (Errno::EACCES)" – AnApprentice Dec 09 '10 at 18:34
  • if I boot locally to prod with port 3000, and enter the URL: "my-domain-name.com:3000", i then get an SSL error that prevents a connections "SSL connection error", lots of these "ERROR bad Request-Line `??M!["9\??p?3?Ͻ$x?7ع?Y?YZ??OH?'." – AnApprentice Dec 09 '10 at 18:35
  • Thank you for the correction re `/etc/hosts` syntax. I have modified my answer to fix the `/etc/hosts` example. – yfeldblum Dec 09 '10 at 21:13
  • Make sure that `config.middleware.use Rack::Www` comes before `config.middleware.use Rack::SSL` in your production environment. Or use methods like `config.middleware.insert_before` etc. instead of `config.middleware.use`. – yfeldblum Dec 09 '10 at 21:15
  • Unless you have a reverse proxy with SSL support (like apache, nginx) proxying to your app, you will not be able to test SSL support locally. Don't worry too much about that.... Comment out that line in the environment file, and just test that `Rack::Www`, as given in my example and customized to your needs, works. – yfeldblum Dec 09 '10 at 21:22
3

Pivotal Labs has some middleware called Refraction that is a mod_rewrite replacement, except it lives in your source code instead of your Apache config.

It may be a little overkill for what you need, but it handles this stuff pretty easily.

Jeff Wigal
  • 708
  • 4
  • 9
1

In Rails 3

#config/routes.rb
Example::Application.routes.draw do
  redirect_proc = Proc.new { redirect { |params, request|
    URI.parse(request.url).tap { |x| x.host = "www.example.net"; x.scheme = "https" }.to_s
  } }
  constraints(:host => "example.net") do
    match "(*x)" => redirect_proc.call
  end
  constraints(:scheme => "http") do
    match "(*x)" => redirect_proc.call
  end
  # .... 
  # .. more routes ..
  # ....
end
Vikrant Chaudhary
  • 10,509
  • 10
  • 51
  • 67
0

Your best bet would be to set up redirect with your DNS provider, so it happens long before any request reaches your server. From the Heroku Dev Center:

Subdomain redirection results in a 301 permanent redirect to the specified subdomain for all requests to the naked domain so all current and future requests are properly routed and the full www hostname is displayed in the user’s location field.

DNSimple provides a convenient URL redirect seen here redirecting from the heroku-sslendpoint.com naked domain to the www.heroku-sslendpoint.com subdomain. enter image description here

For proper configuration on Heroku the www subdomain should then be a CNAME record reference to yourappname.herokuapp.com.

It's not just DNSimple that does this. My DNS provider is 123 Reg and they support it but call it web forwarding.

Mike
  • 9,292
  • 5
  • 41
  • 60
0

I think the issue is you are running on Heroku. Check the Heroku documentation regarding Wildcard domains:

"If you'd like your app to respond to any subdomain under your custom domain name (as in *.yourdomain.com), you’ll need to use the wildcard domains add-on. ..."

$ heroku addons:add wildcard_domains

Also look at Redirecting Traffic to Specific Domain:

"If you have multiple domains, or your app has users that access it via its Heroku subdomain but you later switched to your own custom domain, you will probably want to get all users onto the same domain with a redirect in a before filter. Something like this will do the job:"

class ApplicationController
  before_filter :ensure_domain

  TheDomain = 'myapp.mydomain.com'

  def ensure_domain
    if request.env['HTTP_HOST'] != TheDomain
      redirect_to TheDomain
    end
  end
end
Steve Wilhelm
  • 5,930
  • 1
  • 28
  • 36
0

Try this

def check_uri
  if Rails.env == 'production' && request && (request.subdomains.first != "www" || request.protocol != 'https://')
    redirect_to "https://www.mysite.com" + request.path, :status => 301 and return
  end
end
egze
  • 2,980
  • 18
  • 23