How to Configure Nginx Reverse Proxy with SSL Client Certificate Authentication for Backend Services


2 views

When implementing secure service-to-service communication, we often face scenarios where:

  • Frontend server (Server A) requires client certificate authentication
  • Backend service (Server B) needs to communicate through a local proxy
  • Client certificate must be properly injected during proxy pass

For Server B's nginx configuration (acting as reverse proxy):


server {
    listen 8080;
    server_name localhost;

    location / {
        proxy_pass https://serverA.example.com:443;
        proxy_ssl_certificate /path/to/client.crt;
        proxy_ssl_certificate_key /path/to/client.key;
        proxy_ssl_trusted_certificate /path/to/ca.crt;
        proxy_ssl_verify on;
        proxy_ssl_verify_depth 2;
        proxy_ssl_session_reuse on;
        
        # Optional headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Ensure your certificate files are in proper format:


# Convert PEM to DER if needed
openssl x509 -in client.pem -outform der -out client.crt

# Verify key file
openssl rsa -in client.key -check

When debugging SSL handshake failures:


# Check nginx error logs
tail -f /var/log/nginx/error.log

# Test connection manually
openssl s_client -connect serverA:443 -cert client.crt -key client.key -CAfile ca.crt

For more complex scenarios:


# Multiple client certificates
proxy_ssl_certificate /path/to/client_chain.pem;

# Specific SSL protocols
proxy_ssl_protocols TLSv1.2 TLSv1.3;

# Cipher suite control
proxy_ssl_ciphers HIGH:!aNULL:!MD5;
  • Set proper file permissions (600 for key files)
  • Regularly rotate certificates
  • Monitor certificate expiration dates
  • Consider using hardware security modules (HSM) for production

When implementing secure inter-server communication with mutual TLS authentication, we often encounter scenarios where an internal service needs to communicate through an Nginx proxy while presenting a client SSL certificate. The key challenge is propagating the client certificate through the proxy chain.

First, let's establish our upstream server configuration that requires client certificate authentication:


server {
    listen 443 ssl;
    server_name serverA.example.com;
    
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    
    # Require client certificate
    ssl_client_certificate /etc/nginx/ssl/ca.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;
    
    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header X-Client-Cert $ssl_client_cert;
    }
}

Now, let's configure the reverse proxy on Server B that will forward requests while injecting the client certificate:


server {
    listen 8080;
    server_name localhost;
    
    location / {
        proxy_pass https://serverA.example.com;
        
        # SSL configuration for outbound connection
        proxy_ssl_certificate /etc/nginx/ssl/client.crt;
        proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
        
        # Verify upstream server's certificate
        proxy_ssl_trusted_certificate /etc/nginx/ssl/ca.crt;
        proxy_ssl_verify on;
        proxy_ssl_verify_depth 2;
        
        # Preserve client certificate information
        proxy_set_header X-Original-Client-Cert $http_x_client_cert;
    }
}

For this setup to work properly, ensure:

  • The client certificate (client.crt) is signed by a CA trusted by Server A
  • The Server A's certificate is trusted by Server B (for proxy_ssl_verify)
  • All certificates include the full chain if necessary

When troubleshooting SSL handshake issues between the proxy and upstream server:


# Check SSL handshake details
openssl s_client -connect serverA.example.com:443 -cert client.crt -key client.key -CAfile ca.crt

# Verify Nginx error logs
tail -f /var/log/nginx/error.log

# Test proxy connectivity
curl -v http://localhost:8080 --header "X-Client-Cert: $(awk 'NF {sub(/\\r/, ""); printf "%s\\\\n",$0;}' client.crt)"

For cases where you need to pass through the original client certificate from an external client:


location / {
    proxy_pass https://serverA.example.com;
    proxy_ssl_certificate /etc/nginx/ssl/client.crt;
    proxy_ssl_certificate_key /etc/nginx/ssl/client.key;
    
    # Pass through the original client certificate if present
    if ($http_x_ssl_cert) {
        proxy_set_header X-SSL-CERT $http_x_ssl_cert;
    }
}