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

Nginx

Nginx Problem Overview


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;
}

Nginx Solutions


Solution 1 - Nginx

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

}

Solution 2 - Nginx

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;

  [....]
}

Solution 3 - Nginx

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...
}

Solution 4 - Nginx

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

Solution 5 - Nginx

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

Solution 6 - Nginx

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!

Solution 7 - Nginx

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.

Solution 8 - Nginx

if use phpmyadmin add: fastcgi_param HTTPS on;

Solution 9 - Nginx

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)

Solution 10 - Nginx

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/;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJohnnyView Question on Stackoverflow
Solution 1 - NginxbobojamView Answer on Stackoverflow
Solution 2 - NginxMichael J. EvansView Answer on Stackoverflow
Solution 3 - NginxAlexander AzarovView Answer on Stackoverflow
Solution 4 - NginxKomuView Answer on Stackoverflow
Solution 5 - NginxRemizView Answer on Stackoverflow
Solution 6 - NginxMadan SapkotaView Answer on Stackoverflow
Solution 7 - Nginxrapan iosifView Answer on Stackoverflow
Solution 8 - NginxRodrigo GregorioView Answer on Stackoverflow
Solution 9 - NginxAlireza FarView Answer on Stackoverflow
Solution 10 - NginxIshanView Answer on Stackoverflow