Client Certificate Authentication — Issues and Mitigations

Firefox does not send client certificates on AJAX requests

When using nginx ssl_verify_client on with client certificate authentication, Firefox fails to send the client certificate on AJAX (XHR) requests, even though the initial page load successfully negotiates it. Chromium-based browsers (Chrome, Edge) do not have this issue.

Symptoms

Root cause

This is a confirmed Firefox bug/behavior. Firefox does not reliably send client certificates on XHR/fetch requests, particularly when it opens a new TLS connection for the AJAX call. The withCredentials=true XHR option is documented to help for cross-origin requests, but this also affects same-origin AJAX in some configurations.

Diagnosis steps we followed

  1. Browser DevTools showed /ajax/bases POST returning 400 with 237 bytes
  2. Added add_header X-Debug-Server "ldap-block" always; to the location block — header was present on page load but missing on AJAX (confirmed rejection at TLS level, before HTTP processing)
  3. Changed ssl_verify_client on to optional with if ($ssl_client_verify != SUCCESS) { return 403; } — AJAX returned 403, confirming Firefox wasn't sending the cert
  4. Tested in Chromium — worked perfectly, confirming Firefox-specific issue
  5. curl without client cert reproduced the exact 237-byte nginx response

Mitigation

Use Chromium-based browsers (Chrome, Edge) for accessing client-cert-protected web applications that rely on AJAX.

For this setup, we chose to keep ssl_verify_client on and use Chromium for phpLDAPadmin access, maintaining full client cert security.