2012年12月26日 星期三

Nginx X-Forwarded-Protocol and X-Forwarded-For

I have a client that had multiples apache web servers sit behind the Nginx web load balancer. Currently both http and https requests are terminated on Nginx and the requests will then be proxied to the backend apache web servers at port 80 (i.e. http).

From backend web servers perspective, all traffic coming in are sort of masqueraded by the Nginx , web server could only see the requests are made by Nginx and the protocol was http. So my client would interest to know which protocol the original request was, whether it is http or the ssl-encrypted https.

Nginx do allow customization on proxy header via proxy_set_header attributes. So I added below parameter to the location block so that extra header will be passed to the backend web server.


Here is the reverse proxy configuration
    upstream backend_web_server_pool {
       server 1.2.3.4:80;

       server 1.2.3.5:80;
    }

Here is the http site configuration

server {
    listen       80;  # The http server

    ....     
   location / {
       proxy_pass http://backend_web_server_pool;
       proxy_set_header X-Forwarded-Protocol "http" ;
       proxy_set_header X-Forwarded-For $remote_addr;
    }

}




Here is the https site configuration
server {
    listen       443; 
# The https server
    ssl                  on;
    ....

    location / {
       proxy_pass http://backend_web_server_pool;
       proxy_set_header X-Forwarded-Protocol "https" ;
       proxy_set_header X-Forwarded-For $remote_addr;
    }

}


So the line proxy_set_header X-Forwarded-Protocol "http" will pass a header named "X-Forwarded-Protocol" and its value "http" to the backend web server. You can replace this header value to any arbitrary value, e.g. "xyz123". After all it is just a placeholder or symbol to let you know where the request came from. The same logic applies to the HTTPS block however you may want to replace the value from "http" to "https" to avoid confusion. The line proxy_set_header X-Forwarded-For $remote_addr pass the variable remote_addr (i.e. the remote client IP address) to the backend web server.

Once the above configuration applied, restart nginx and then we can head to reconfigure the log format configuration on apache web server. We will now modify the combined log format to capture the X-Forwarded-For and X-Forwarded-Protocol.

#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %{X-Forwarded-For}i %{X-Forwarded-Protocol}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined


I added the %{X-Forwarded-For}i and %{X-Forwarded-Protocol}i to the combined log format followed by apache restart and now apache log could capture the client IP address and the original protocol.