NGINX.COM

Configuring NGINX to accept the PROXY Protocol

This article explains how to configure NGINX and NGINX Plus to accept the PROXY protocol, rewrite the IP address of a load balancer or proxy to the one received in the PROXY protocol header, configure simple logging of a client’s IP address, enable the PROXY protocol between NGINX and TCP upstream server.

Table of Contents

Introduction

The PROXY protocol enables NGINX and NGINX Plus to receive client connection information from proxy servers and load balancers such as HAproxy and Amazon Elastic Load Balancer (ELB).

With the PROXY protocol, NGINX can learn the originating IP address from HTTP, SSL, HTTP/2, SPDY, WebSocket, and TCP. Knowing the originating IP address of a client may be useful for setting a particular language for a website, keeping a blacklist of IPs, or simply for logging and statistics purposes.

The data passed via the PROXY protocol is the client IP address, the proxy server IP address, and both port numbers. Using this data, NGINX can get the originating IP address of the client in several ways:

  • with the $proxy_protocol_addr and $proxy_protocol_addr_port variables that keep original client IP address and port. The $remote_addr and $remote_port variables will keep the IP and port of the load balancer.
  • with the realip module that will rewrite the $remote_addr, $remote_port variables from the IP and port of the load balancer to the original client IP address and port. The $realip_remote_addr and $realip_remote port variables will keep for the address and port of the load balancer, the $proxy_protocol_addr and $proxy_protocol_port variables will keep the original client IP and port anyway.

Prerequisites

Configuring NGINX to Accept the PROXY Protocol

To configure NGINX to accept the PROXY protocol headers, add the proxy_protocol parameter to the listen directive for http or stream:

http {
    ...
        server {
        listen 80   proxy_protocol;
        listen 443  ssl proxy_protocol;
        ...
    }
}

or for a TCP stream:

stream {
    ...
        server {
        listen 12345   proxy_protocol;
        ...
    }
}

Now you can use the $proxy_protocol_addr and $proxy_protocol_port variables for the client IP address and port and additionally configure the HTTP realip or stream realip module to replace the IP of the load balancer to the IP of the client in the $remote_addr and $remote_port variables.

Changing Load Balancer’s IP Address To Client IP Address

You can replace the address of the load balancer or TCP proxy to the client IP address received from the PROXY protocol. This can be done with the ngx_http_realip_module and ngx_stream_realip_module modules. With these modules, the $remote_addr, $remote_port variables will keep the real IP address and port of the client, while the $realip_remote_addr and $realip_remote_port variables will keep the IP address and port of the load balancer.

    To change the IP address from the load balancer’s IP to client’s IP:

  1. Make sure you’ve configured NIGNX to accept the PROXY protocol headers. See Configuring NGINX to Accept the PROXY Protocol.
  2. Make sure that your NGINX installation includes the HTTP realip and stream realip modules:

    nginx -V 2>&1 | grep -- 'http_realip_module'
    nginx -V 2>&1 | grep -- 'stream_realip_module'

    If not, recompile NGINX with these modules. See Installing NGINX Open Source for details. For NGINX Plus, no extra installation steps required.

  3. Specify the IP address or the CIDR range of addresses of the TCP proxy or load balancer with the set_real_ip_from directive for http or stream:

    server {
        ...
        set_real_ip_from 192.168.1.0/24;
        ...
    }
  4. For http {}, change the IP address of the load balancer to the IP address of the client received from the PROXY protocol header. In the real_ip_header directive, specify the proxy_protocol parameter:

    server {
        ...
        real_ip_header proxy_protocol;
    }

Logging the Original IP Address

When you know the original IP address of the client, you can configure the correct logging:

  1. For http {}, pass the client IP address from NGINX to an upstream server with the proxy_set_header directive and the $proxy_protocol_addr variable:

    proxy_set_header X-Real-IP       $proxy_protocol_addr;
    proxy_set_header X-Forwarded-For $proxy_protocol_addr;
  2. Add the $proxy_protocol_addr variable to the log_format directive for http or stream:

    • for the http {} block:

      http {
          ...
          log_format combined '$proxy_protocol_addr - $remote_user [$time_local] '
                              '"$request" $status $body_bytes_sent '
                              '"$http_referer" "$http_user_agent"';
      }
    • for the stream {} block:

      stream {
          ...
          log_format basic '$proxy_protocol_addr - $remote_user [$time_local] '
                              '$protocol $status $bytes_sent $bytes_received '
                               '$session_time';
      }

PROXY Protocol for a TCP Connection to an Upstream

For a TCP stream, the PROXY protocol can be enabled for connections between NGINX and an upstream server. To enable the PROXY protocol, include the proxy_protocol directive in the server block on the stream {} level:

stream {
    server {
        listen 12345;
        proxy_pass example.com:12345;
        proxy_protocol on;
    }
}

Example

http {
    log_format combined '$proxy_protocol_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent"';
    ...

    server {
        server_name localhost;

        listen 80   proxy_protocol;
        listen 443  ssl proxy_protocol;

        ssl_certificate      /etc/nginx/ssl/public.example.com.pem;
        ssl_certificate_key  /etc/nginx/ssl/public.example.com.key;

        location /app/ {
            proxy_pass                       http://backend1;
            proxy_set_header Host            $host;
            proxy_set_header X-Real-IP       $proxy_protocol_addr;
            proxy_set_header X-Forwarded-For $proxy_protocol_addr;
        }
    }
}

stream {
    log_format basic '$proxy_protocol_addr - $remote_user [$time_local] '
                     '$protocol $status $bytes_sent $bytes_received '
                     '$session_time';
...
    server {

        listen              12345 ssl proxy_protocol;

        ssl_certificate     /etc/nginx/ssl/cert.pem;
        ssl_certificate_key /etc/nginx/ssl/cert.key;

        proxy_pass          backend.example.com:12345;
        proxy_protocol      on;
    }

}

The example assumes that there is a load balancer in front of NGINX, for example, Amazon ELB that balances all incoming HTTPS traffic. NGINX accepts the HTTPS traffic on port 443 , TCP traffic on port 12345, and the PROXY protocol (the proxy_protocol parameter of the listen directive in both http {} and stream {} blocks).

NGINX terminates the HTTPS traffic (the ssl_certificate and ssl_certificate_key directives) and proxies the decrypted data to a backend server (proxy_pass http://backend1; for http {}, proxy_pass backend.example.com:12345 for stream {}) including the client’s IP address and port (the values of the proxy_set_header directives).

The proxy_protocol_addr variable specified in the log_format directive also passes the client’s IP address to the log both for http {} and stream {}.

Additionally, a TCP server (the stream {} block) sends its own PROXY protocol traffic to its backend servers (the proxy_protocol on directive).

TRY NGINX PLUS!

Download a 30 day free trial and see what you've been missing.



X

Got a question for the NGINX team?

< back


X

Sign up for beta

We'll be in touch with you about our NGINX Controller beta.



X