7

I want to run my entire Rails app with SSL, so I thought to use Rails' global force_ssl config option, which works great, except that the ELB's healthchecker will never work because if I set it to http, Rails will forward to https with a 301, and the health check will fail since it is not 200. If I set it to https, nginx/rails will be unable to handle the request, since SSL is handled by the ELB and nginx/rails is only handling HTTP.

My non-ideal solution would be to make an exception to the global force_ssl just for the health check page, but Rails' global force_ssl config always overrides the force_ssl :except => :health_check so that doesn't seem to work.

Another solution would be to not use ELB for SSL termination, and set up HAProxy or so, but I want to use Amazon's infrastructure as much as possible for things, to focus more on core development of the project instead of infrastructure.

This is my first serverfault post, so I appreciate any help I can get (or more information I can give). Thanks.

Update:

So far I "resolved" this by having the ELB access the EC2 instance over a different port than 80 that only it can access, which feels extreme, but keeps a separation between the application and server layers. If the healthcheck request comes from the ELB from this port, nginx will forward the X-Forwarded-Proto header set to 'https' which will let Rack think that it came over SSL, and let it through. For all other traffic coming in across the standard port 80, it just forwards the X-Forwarded-Proto header given by the ELB, which will accurately report what the external user was using, and let Rails decide to force https or not depending.

Still awaiting a cleaner solution, but this is what I have.

4 Answers 4

5

6 months later, here is your cleaner solution.

# config/environments/production.rb
config.ssl_options = { exclude: proc { |env| env['PATH_INFO'].start_with?('/health_check') } }
3

It should be noted that the exclude option to config.ssl_options is now deprecated and you have to use the rack-ssl gem to get the same behavior.

It didn't seem like a good idea to me to include and initialize a new rack middleware just for the health check, so I decided to use nginx instead to set the $http_x_forwarded_proto header for the health checker.

Here's what I came up with:

location @unicorn {

  if ($http_user_agent ~ "ELB-HealthChecker")
  {
    set $http_x_forwarded_proto https;
  }

  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
  proxy_set_header Host $http_host;
  proxy_redirect off;
  proxy_pass http://unicorn;
}

Now rails sees the Health Checker requests as https (though they are not) and returns success.

0

After days with wrestling with configs, etc I found this great gem:

https://github.com/lserman/aws-healthcheck

No need for any custom nginx setup (it works through rack). It returns 200 on /healthcheck which works like a charm. Just add it to your gems and you are done.

-1

f you don't actually care if your health check hits your Rails app (which you probably should...) then you can point it at a static file, like /robots.txt and use HTTP / 80 instead of HTTPS / 443. Nginx will serve up the static file without ever involving Rails.

Not that I really recommend this long-term, but if you're struggling to get anything to come through via your ELB, this will help troubleshoot.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .