Nginx, vhosts and client certificates

Using client certificates with multiples vhosts might show limitations.

If a deployment consists of:

  • A first layer of Nginx reverse proxies, for TLS termination;
  • Connected, with TLS client certificates, to a set of backend "application" servers;
  • And the Host / SNI headers sent to the backend don't match that sent to the client (for instance when using a blue/green deployment at the front layer);
  • And the backend servers host several vhosts with ssl_verify_client enabled on the same IP;

Then users might encounter a 421 Misdirected Request error (at least under nginx version 1.14.2.

This is quite reasonable: Nginx receives a Host: header matching the public name (let's say, but sees a TLS connection to SNI host

Since the TLS hostname doesn't match the requested Host header, it assumes that the client is reusing a connection intended for another vhost, and the client certificate verification isn't valid.


Possible solutions:

  • Use a dedicated IP address for each client-SSL-enabled vhost;

  • Don't send the public Host: header, use the internal one instead;

  • Perform hostname-based request routing in code:

    map $ssl_server_name $dyn_upstream {
      default             "none";  "public";    "priv";
    server {
      listen 443 ssl;
      if ($dyn_upstream = "none") {
        # Early request termination
        return 444;
      location / {
        upstream http://$dyn_upstream/;
  • Stack two vhosts: the first one performs client certificate verification, and adds metadata to the request headers; an upstream instance (could be the same nginx process) will then perform perform the routing, examining the forwarded headers for client checks.

Previous posts

  1. Installing Python packages in $HOME
  2. Patching across Python implementations
  3. Setting up a private, team-wide PyPI repository
    System Python
  4. Getting Firefox/Chrome to use system-wide certificates
    System Web Linux Gentoo
  5. Xel.Mind — Random thoughts