How to solve 400 bad request error when using nginx as reverse proxy ?

Problem

The original nginx.conf file content

http { 
  upstream myserver {
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
    server 127.0.0.1:8003;
  }

  server {
    listen 8000;
    server_name 127.0.0.1;
    location / {
      proxy_pass http://myserver;
    }
  }
}

I run three http server instances in one server, and I use nginx as reverse proxy to load balancing the request to backend http services.

image-20200910145002711

When I start nginx, I got this:

HTTP/1.1 400 Bad Request => 
Server => nginx

I use curl to debug the http request and response:

curl -v http://127.0.0.1:8000/
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
    > GET / HTTP/1.1
    > Host: 127.0.0.1:8000
    >
    < HTTP/1.1 400 Bad Request
    < Server: nginx
    ....
    <
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
    <HTML><HEAD><TITLE>Bad Request</TITLE>
    <META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
    <BODY><h2>Bad Request</h2>
    <hr><p>HTTP Error 400.</p>
    </BODY></HTML>

Solution #1

When nginx returns 400 (Bad Request) it will log the reason into error log, at “info” level. Hence an obvious way to find out what’s going on is to configure error_log to log messages at “info” level and take a look into error log when testing.

You should open the debug log by adding this line to nginx.conf:

    error_log /var/log/nginx/error.log debug;

Solution #2

Some server may require additional http headers to be set in the http request. You can add some useful http headers by using proxy_set_header like this:

proxy_set_header Host      $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

According to mozilla document:

The Host Header Specifies the domain name of the server (for virtual hosting), and (optionally) the TCP port number on which the server is listening.

X-Real-IP Let nginx pass the request to backend with real client IP address. You should know that:

This module is not built by default, it should be enabled with the --with-http_realip_module configuration parameter.

X-Forwarded-For Identifies the originating IP addresses of a client connecting to a web server through an HTTP proxy or a load balancer.

The final nginx.conf

error_log /var/log/nginx/error.log debug;

http {
    upstream myserver {
        server 127.0.0.1:8001;
        server 127.0.0.1:8002;
        server 127.0.0.1:8003;
    }
    server {
        listen       8000;
        server_name  127.0.0.1;

        location / {
           proxy_pass http://myserver/;

           proxy_set_header Host      $host:$server_port;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	       
        }
      }
}