Nginx – How to only serve https using passenger 2.2.9, nginx 0.7.65, and sinatra 0.94

nginxphusion-passengerssl

I'm trying to deploy a website with only https access, no http access. I'm sure that I'm just missing some simple line in a configuration file.

As the title says, it's Sinatra 0.9.4 on nginx 0.7.65 through passenger 2.2.9.

The main ruby file for the Sinatra service has:

require 'ssl_requirement'

def ssl_required?
  true
end

config.ru has (although I'm not sure it's relevant):

require 'rubygems'
require 'sinatra'
require 'app.rb'

set :environment, :production
run Sinatra::Application

The nginx.conf file looks like:

 worker_processes  1;


 events {
   worker_connections  1024;
 }


 http {
   passenger_root /var/lib/gems/1.8/gems/passenger-2.2.9;
   passenger_ruby /usr/bin/ruby1.8;

   include       mime.types;
   default_type  application/octet-stream;


   # HTTPS server
   #
   server {
     listen       80;
     listen       443;
     server_name  an.internal.ip.address;
     root         /my/app/dir/public;
     passenger_enabled on;

     proxy_set_header X_FORWARDED_PROTO https;

     ssl                  on;
     ssl_certificate      /my/app/dir/certificate.pem;
     ssl_certificate_key      /my/app/dir/privkey.pem;

     ssl_session_timeout  5m;

     ssl_protocols  SSLv2 SSLv3 TLSv1;
     ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
     ssl_prefer_server_ciphers   on;

   }

 }

Instead of redirecting all http requests to https, I instead get the error:

The plain HTTP request was sent to HTTPS port

How can I fix this? https requests to https://an.internal.ip.address work fine.

(it also happens with nginx-0.6.36, for what that's worth).

Not sure if this should get tagged with Sinatra, but I don't have enough rep for that yet.

Best Answer

if you type in http://server/ the browser sends plain text request to the server on port 80. whereas if you type in https://server/ it'll send encrypted request to port 443. what you're doing is you sending plain text to https, so webserver expects encrypted, but gets plain.

it'd work if you did https://server:80/, so webrowser knows it needs to encrypt, but sends to 80. i doub't you can expect that from users.

what you want to is, accept unencrypted traffic on 80, and just http redirect all requests to 443. in nginx:

server {
  listen server_ip:80;
  server_name server_name;
  location / {
    rewrite ^/(.*) https://server_name permanent;
  }
}