NGINX Proxy Host Header: A Practical Guide

Updated: January 19, 2024 By: Guest Contributor Post a comment

Introduction

Welcome to this practical guide on how to correctly use and manipulate the Host header in NGINX when configuring proxy server settings. The Host header is critical to virtual host routing and security. This tutorial covers why the Host header is important, how NGINX handles it in a reverse proxy setup, and examples from basic to advanced on configuring it for your needs.

Understanding the Host Header

The Host header is a core part of HTTP requests. It specifies the domain name or IP address of the server to which the request is being sent. Reverse proxy servers like NGINX use this header to determine how to route requests to backend servers.

Basic NGINX Configuration

Let’s start with a basic configuration where NGINX acts as a reverse proxy for a single backend server:

http {
    server {
        listen 80;
        server_name myapp.example.com;

        location / {
            proxy_pass http://backend_server_ip;
        }
    }
}

In this setup, NGINX passes the original Host header to the backend server unmodified. This behavior is suitable when the backend server expects to receive requests for the same host that the client used to reach the reverse proxy.

Changing the Host Header

Sometimes, the backend server expects a specific Host header. You can modify it like so:

http {
    server {
        #...
        location / {
            proxy_set_header Host $proxy_host;
            proxy_pass http://backend_server_ip;
        }
    }
}

This configuration will pass the proxy’s host and port instead of the client’s original Host header.

Preserving the Original Host Header

If you need to preserve the client’s Host header while still able to add custom headers, you could use the following setup:

http {
    server {
        #...
        location / {
            proxy_set_header Host $http_host;
            proxy_pass http://backend_server_ip;
        }
    }
}

This preserves whatever host was specified in the original client request.

Advanced Host Header Manipulation

In more complex setups, you might need to selectively change the Host header based on the incoming request. You can utilize map directive for this:

http {
    map $http_host $custom_host {
        hostnames;
        default                 $http_host;
        myapp.example.com       backend.internal;
    }

    server {
        #...
        location / {
            proxy_set_header Host $custom_host;
            proxy_pass http://backend_server_ip;
        }
    }
}

Here, the Host header is set to backend.internal when the request Host matches myapp.example.com, otherwise, it preserves the client’s Host header.

Using Variables and Conditional Logic

NGINX allows for more sophisticated adjustments with variables and if directives. For instance, deciding on the Host header to pass based on a particular URI:

http {
    server {
        #...
        set $custom_host '';
        if ($request_uri ~* '^/special-path') {
            set $custom_host 'special-backend.internal';
        }
        location / {
            proxy_set_header Host $custom_host;
            proxy_pass http://backend_server_ip;
        }
    }
}

Keep in mind that the use of if can be tricky in NGINX and is best avoided when possible.

SSL/TLS and the Host Header

When working with SSL/TLS, you may need to ensure that the correct Host header is used for certificate verification. This is straightforward in NGINX:

http {
    server {
        listen 443 ssl;
        server_name myapp.example.com;

        ssl_certificate /path/to/certificate.pem;
        ssl_certificate_key /path/to/private_key.pem;

        location / {
            proxy_set_header Host $http_host;
            proxy_pass https://backend_server_ip;
        }
    }
}

The ssl_certificate and ssl_certificate_key directives specify the location of your SSL/TLS certificates. The Host header here ensures that the backend server can perform hostname-based actions or logging accurately.

Real-World Scenario: Handling Multiple Subdomains

To better understand the practical aspects of the Host header configuration, here’s an example of an NGINX reverse proxy handling requests for multiple subdomains:

http {
    map $http_host $backend_url {
        hostnames;
        default                http://default_backend;
        sub1.example.com       http://backend1;
        sub2.example.com       http://backend2;
    }

    server {
        listen 80;
        server_name sub1.example.com sub2.example.com;

        location / {
            proxy_pass $backend_url;
            proxy_set_header Host $http_host;
        }
    }
}

In this configuration, NGINX chooses the correct backend server based on the requested subdomain and forwards the appropriate Host header.

Debugging Host Header Issues

Debugging is an essential aspect of working with NGINX proxies. If you experience issues, make sure that NGINX is forwarding the correct Host header with the proxy_pass and proxy_set_header directives. Enabling logging with error_log and access_log could also help pinpoint the problem.

Conclusion

To sum up, properly handling the Host header in NGINX is vital for reverse proxy configurations. Through various examples, we’ve demonstrated how to preserve the original Headerr or set a custom one based on your application’s needs. Remember to test configurations in a development environment before pushing them into production to avoid downtime.