Johnny
Johnny

Reputation: 7323

Dealing with nginx 400 "The plain HTTP request was sent to HTTPS port" error

I'm running a Sinatra app behind passenger/nginx. I'm trying to get it to respond to both http and https calls. The problem is, when both are defined in the server block https calls are responded to normally but http yields a 400 "The plain HTTP request was sent to HTTPS port" error. This is for a static page so I'm guessing Sinatra has nothing to do with this. Any ideas on how to fix this?

Here's the server block:

server {
        listen 80;
        listen 443  ssl;
        server_name localhost;
        root /home/myhome/app/public;
        passenger_enabled on;

        ssl on;
        ssl_certificate      /opt/nginx/ssl_keys/ssl.crt;
        ssl_certificate_key  /opt/nginx/ssl_keys/ssl.key;
        ssl_protocols        SSLv3 TLSv1;
        ssl_ciphers          HIGH:!aNULL:!MD5;

        location /static {
            root  /home/myhome/app/public;
            index  index.html index.htm index.php;
        }

        error_page 404 /404.html;

        # redirect server error pages to the static page /50x.html
        error_page 500 /500.html;

        access_log /home/myhome/app/logs/access.log;
        error_log /home/myhome/app/logs/error.log;
}

Upvotes: 153

Views: 321480

Answers (12)

Omid Estaji
Omid Estaji

Reputation: 321

Other answers are correct, But I want to note some other aspect of the problem. I had the same issue when I was reverse proxy a reverse proxy, I mean I was two different Nginx back to back. The problem was the first Nginx and when I removed the first Nginx the problem solved.

Upvotes: 0

Ivan
Ivan

Reputation: 41

In my case just those lines help me:

   if ($scheme = 'http') {
        return 301 https://$server_name$request_uri;
    }

Upvotes: 0

Alireza Far
Alireza Far

Reputation: 31

its error 497 and not error 400 . you can handle Error 497 like this and redirect http to https with a 301 (moved permanently) or 302 (moved temporary) like this:

error_page 497 = @foobar;

location @foobar {
return 301 https://$host:$server_port$request_uri;
}

this will redirect you to the exact url you requested but replace your request 'http' with 'https' with no error or confirm (its 301 redirect and counts as seo safe)

Upvotes: 2

Ishan
Ishan

Reputation: 720

In my case my response redirected from jenkins to 443

Just added a proxy redirect in nginx configuration to make it work

proxy_redirect http://test.example.com:443/ https://test.example.com/;

Upvotes: 0

Michael J. Evans
Michael J. Evans

Reputation: 781

The above answers are incorrect in that most over-ride the 'is this connection HTTPS' test to allow serving the pages over http irrespective of connection security.

The secure answer using an error-page on an NGINX specific http 4xx error code to redirect the client to retry the same request to https. (as outlined here https://serverfault.com/questions/338700/redirect-http-mydomain-com12345-to-https-mydomain-com12345-in-nginx )

The OP should use:

server {
  listen        12345;
  server_name   php.myadmin.com;

  root         /var/www/php;

  ssl           on;

  # If they come here using HTTP, bounce them to the correct scheme
  error_page 497 https://$server_name:$server_port$request_uri;

  [....]
}

Upvotes: 64

Madan Sapkota
Madan Sapkota

Reputation: 26071

Here is an example to config HTTP and HTTPS in same config block with ipv6 support. The config is tested in Ubuntu Server and NGINX/1.4.6 but this should work with all servers.

server {
    # support http and ipv6
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    # support https and ipv6
    listen 443 default_server ssl;
    listen [::]:443 ipv6only=on default_server ssl;

    # path to web directory
    root /path/to/example.com;
    index index.html index.htm;

    # domain or subdomain
    server_name example.com www.example.com;

    # ssl certificate
    ssl_certificate /path/to/certs/example_com-bundle.crt;
    ssl_certificate_key /path/to/certs/example_com.key;

    ssl_session_timeout 5m;

    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
    ssl_prefer_server_ciphers on;
}

Don't include ssl on which may cause 400 error. The config above should work for

http://example.com

http://www.example.com

https://example.com

https://www.example.com

Hope this helps!

Upvotes: 9

Komu
Komu

Reputation: 14998

According to wikipedia article on status codes. Nginx has a custom error code when http traffic is sent to https port(error code 497)

And according to nginx docs on error_page, you can define a URI that will be shown for a specific error.
Thus we can create a uri that clients will be sent to when error code 497 is raised.

nginx.conf

#lets assume your IP address is 89.89.89.89 and also 
#that you want nginx to listen on port 7000 and your app is running on port 3000

server {
    listen 7000 ssl;
 
    ssl_certificate /path/to/ssl_certificate.cer;
    ssl_certificate_key /path/to/ssl_certificate_key.key;
    ssl_client_certificate /path/to/ssl_client_certificate.cer;

    error_page 497 301 =307 https://89.89.89.89:7000$request_uri;

    location / {
        proxy_pass http://89.89.89.89:3000/;

        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}

However if a client makes a request via any other method except a GET, that request will be turned into a GET. Thus to preserve the request method that the client came in via; we use error processing redirects as shown in nginx docs on error_page

And thats why we use the 301 =307 redirect.

Using the nginx.conf file shown here, we are able to have http and https listen in on the same port

Upvotes: 18

rapan iosif
rapan iosif

Reputation: 41

Actually you can do this with:

ssl off; 

This solved my problem in using nginxvhosts; now I am able to use both SSL and plain HTTP. Works even with combined ports.

Upvotes: 4

bobojam
bobojam

Reputation: 2465

I ran into a similar problem. It works on one server and does not on another server with same Nginx configuration. Found the the solution which is answered by Igor here http://forum.nginx.org/read.php?2,1612,1627#msg-1627

Yes. Or you may combine SSL/non-SSL servers in one server:

server {
  listen 80;
  listen 443 default ssl;

  # ssl on   - remember to comment this out

}

Upvotes: 211

Remiz
Remiz

Reputation: 400

I had the exact same issue, I have kind of the same configuration as your exemple and I got it working by removing the line :

ssl on;

To quote the doc:

If HTTP and HTTPS servers are equal, a single server that handles both HTTP and HTTPS requests may be configured by deleting the directive “ssl on” and adding the ssl parameter for *:443 port

Upvotes: 12

Rodrigo Gregorio
Rodrigo Gregorio

Reputation: 91

if use phpmyadmin add: fastcgi_param HTTPS on;

Upvotes: 4

Alexander Azarov
Alexander Azarov

Reputation: 13221

The error says it all actually. Your configuration tells Nginx to listen on port 80 (HTTP) and use SSL. When you point your browser to http://localhost, it tries to connect via HTTP. Since Nginx expects SSL, it complains with the error.

The workaround is very simple. You need two server sections:

server {
  listen 80;

  // other directives...
}

server {
  listen 443;

  ssl on;
  // SSL directives...

  // other directives...
}

Upvotes: 19

Related Questions