2

I'm running a Rails 3.1.4 app that needs to have some pages use force_ssl, but others that I don't want to have SSL. Is there an opposite to force_ssl, like force_http? The issue is that I have some pages that don't need to be SSL, and they contain elements that I can't pull over using https (so the user gets the insecure error from the browser).

What happens is that when someone goes to a force_ssl page it works fine, but then when they go to other pages where it is not force_ssl, they receive the insecure page error from the browser because the https remains. Therefore, I'd like to force the pages where I can't make the elements https avoid using use SSL (ie force it back to http).

Thank you in advance.

yellowreign
  • 3,528
  • 8
  • 43
  • 80

3 Answers3

5

Ok, I found a way that seems to work for me:

In my sessions controller I added this:

force_ssl :only => [:new, :create]

So only those 2 actions will get called over SSL. Now, after a user successfully logs in, the page will redirect to a non-SSL connection.

As far as forcing the pages to be non-SSL, try this question: Rails 3.1 Force Regular HTTP, look for Joost's answer with a before_filter.

This is what he posted:

class ApplicationController < ActionController::Base
  before_filter do
    if request.ssl && Rails.env.production?
      redirect_to :protocol => 'http://', :status => :moved_permanently
    end
  end
end

Disclaimer: The code above is not mine, I copied it from the question in the link, just putting it here for convenience. Please visit the above link to see it in its original context.

Also, regarding forcing non-SSL, I'm not sure that's a great idea. If a user requests an SSL connection and you have the certificates, why not give it to them? I agree that SSL does put more strain on the browser, but some people like surfing on SSL all the time. If you have some code that does not work over SSL than that is a valid reason to force non-SSL.

Cheers.

Community
  • 1
  • 1
kakubei
  • 5,321
  • 4
  • 44
  • 66
  • I tried to use it, but it forced me to change request.ssl to add a question mark (as someone commented for the answer on the other page). when i went to my force_ssl page i got an error saying there were too many redirects – yellowreign May 18 '12 at 20:12
  • Too many redirects means it's stuck in a loop. What happens if you leave the before_filter out? Do all the pages get served via SSL even when you don't request it? – kakubei May 20 '12 at 09:43
  • No, not by default. What happens is that someone goes to an https (force_ssl) page and all subsequent pages visited via link adopt the https. I decided you are right about just letting these pages render in in https instead of forcing http – yellowreign May 20 '12 at 19:57
  • What browser are you using? Just out of curiosity. It could be a cache situation. If Safari, try this on the command line: dscacheutil -flushcache. Also try Chrome to see what you get. – kakubei May 22 '12 at 06:19
  • @yellowreign if my answer helped you please mark it as accepted. I know it may sound silly, but the points you accrue can then be used to create a bounty when you have tough questions to post. Thanks. – kakubei May 22 '12 at 17:46
  • 1
    it doesn't sound silly, but maybe you can post another answer saying that i should just let it be ssl or something because the code didn't work and i don't want to mislead anyone (leads to a infinite loop of redirects across different browsers). so actually i decided to just let it go – yellowreign May 22 '12 at 19:02
0

Got this to work without a loop. Unless it happens in browsers I am not aware of.

before_filter do
  unless params[:controller] == 'whatever_controller_name_you_are_serving_https_to'
    if request.ssl? && Rails.env.production?
      redirect_to :protocol => 'http://', :status => :moved_permanently
    end
  end
end
Hunt Burdick
  • 517
  • 4
  • 13
0

Assuming (hoping) you've upgraded to a supported version of Rails, this is an easy way to achieve default non-SSL unless force_ssl is declared. It's in the form of an ActiveSupport::Concern:

module ForceNoSSL
  extend ActiveSupport::Concern

  ACTION_OPTIONS = [:only, :except, :if, :unless]

  def force_no_ssl_redirect
    if request.ssl?
      options = {
        :protocol => 'http://',
        :host     => request.host,
        :path     => request.fullpath,
        :status   => :moved_permanently
      }

      redirect_to ActionDispatch::Http::URL.url_for(options)
    end
  end

  class_methods do
    def force_ssl(options = {})
      super

      action_options = options.slice(*ACTION_OPTIONS)
      skip_before_action :force_no_ssl_redirect, **action_options
    end

    def force_no_ssl
      before_action :force_no_ssl_redirect
    end
  end
end

In your ApplicationController you then:

include ForceNoSSL
force_no_ssl

How it works is that by default all actions will run the force_no_ssl_redirect callback. If it's already a non-ssl connection then nothing will be done, otherwise the request will be redirected to http://.

However, if you've declared force_ssl then the force_no_ssl_redirect callback will be skipped (only for those actions specified by the ACTION_OPTIONS).

You could extend this to allow force_no_ssl to accept URL options etc... like the force_ssl action does, but for my use case this wasn't necessary.

P.S. A lot of this code comes from ActionController::ForceSSL so check that out for more insight.

Brendon Muir
  • 4,540
  • 2
  • 33
  • 55