Nginx Client Certs – Verifying Only on Specific Location

nginxssl-certificate

We use Nginx as a reverse proxy to our web application server. Nginx handles our SSL and such but otherwise just acts as a reverse proxy.

We want to require a valid client cert for requests to /jsonrpc but not require them anywhere else. The best way we've found is to

server {
  listen       *:443 ssl;

  ssl on;
  ssl_certificate         /etc/nginx/server.crt;
  ssl_certificate_key     /etc/nginx/server.key;
  ssl_client_certificate  /etc/nginx/client-ca.crt;

  ssl_verify_client optional;

  location /jsonrpc {
    if ($ssl_client_verify != "SUCCESS") { return 403; }

    proxy_pass          http://localhost:8282/jsonrpc-api;
    proxy_read_timeout  90;
    proxy_redirect      http://localhost/ $scheme://$host:$server_port/;
  }
}

This works fine for most browsers, but some browsers such as Safari and Chrome-on-Android end up prompting the user to provide a client cert no matter where on the website they go.

How do we get Nginx to accept but not really care about a client cert everywhere except our /jsonrpc location?

Best Answer

Why not to try second server block instead? Code duplication is bad but sometimes unavoidable. I assume /jsonrpc represents an API so it can use its own subdomain if not already use it:

server {
  listen       *:443 ssl;
  server_name api.example.com;

  ssl on;
  ssl_certificate         /etc/nginx/server.crt;
  ssl_certificate_key     /etc/nginx/server.key;
  ssl_client_certificate  /etc/nginx/client-ca.crt;

  ssl_verify_client on;

  location =/jsonrpc {
    proxy_pass          http://localhost:8282/jsonrpc-api;
    proxy_read_timeout  90;
    proxy_redirect      http://localhost/ $scheme://$host:$server_port/;
  }
}

server {
  listen       *:443 ssl;

  ssl on;
  ssl_certificate         /etc/nginx/server.crt;
  ssl_certificate_key     /etc/nginx/server.key;
  ssl_client_certificate  /etc/nginx/client-ca.crt;

  ssl_verify_client off;

  location / {
    proxy_pass          http://localhost:8282/;
    proxy_read_timeout  90;
    proxy_redirect      http://localhost/ $scheme://$host:$server_port/;
  }
}