Views: 8

No Required SSL Certificate Was Sent (nginx 496)

No required SSL certificate was sent means nginx wanted an mTLS client cert and got none. Isolate client vs server in 3 steps. Free instant check, no sign-up.

Check your domain for this issue now

Free, no sign-up. Runs the exact check this guide describes and shows what to fix.

Problem

Your request to an nginx endpoint comes back 400 Bad Request with the body No required SSL certificate was sent, and the server’s access log records the status as 496. The TLS handshake didn’t fail on the server’s certificate — the padlock side is fine. nginx is enforcing mutual TLS: it asked your client to prove its own identity with a certificate, and your client didn’t send one. This isn’t about the server cert, a cipher, or a name mismatch. It’s a client-authentication requirement you didn’t satisfy. The question is whether your client should be sending a cert and isn’t, or whether something between you and the origin quietly dropped it.

Symptoms

  • The client sees 400 Bad Request with the exact text No required SSL certificate was sent; the nginx access log shows status 496.
  • It only happens against endpoints (a whole host, or specific paths/APIs) configured for client-certificate auth — everything else on the server loads normally.
  • curl reproduces it unless you pass --cert and --key; a browser reproduces it unless the right client certificate is imported and selected at the prompt.
  • Sometimes it’s intermittent — most requests succeed and an occasional one returns 496 on a long-lived or pooled connection (see the FAQ).

What This Error Actually Means

496 is one of nginx’s non-standard internal status codes, defined in its source alongside 495 (a client certificate was presented but failed verification) and 497 (a plain HTTP request arrived on the HTTPS port). 496 — internally NGX_HTTP_NO_CERT — means the client presented no certificate when the server required one. Because 496 isn’t a registered HTTP status, nginx doesn’t put it on the wire; it returns a 400 Bad Request to the client and logs 496 for you.

It appears when a server block enables client-certificate verification with ssl_verify_client on; and names the CA bundle it trusts for client certs with ssl_client_certificate. During the TLS handshake nginx sends a CertificateRequest; if the client answers with an empty certificate list, verification can’t even begin, and nginx returns 496. The related variable $ssl_client_verify is NONE in this case — as opposed to SUCCESS for a good cert or FAILED:<reason> for a rejected one. So 496 is a very specific statement: not “your certificate is bad,” but “you didn’t bring the certificate this endpoint requires.”

Top 3 Causes

  1. The client genuinely isn’t sending a certificate - The most common cause. curl without --cert/--key, an HTTP library whose TLS context has no client cert configured, a browser with the certificate not imported (or the user dismissing the “select a certificate” prompt), or an empty/wrong keystore path. The endpoint requires mTLS and the request simply arrived without credentials.
  2. A reverse proxy or load balancer stripped the certificate - You are sending a cert, but an intermediary terminated TLS before the origin and never forwarded it. A front nginx/LB that re-originates the connection to a backend which also requires mTLS will make the backend see no client cert and return 496. The fix is to pass the cert through (e.g., forwarding $ssl_client_escaped_cert in a header the backend trusts) or to use TLS pass-through so the origin performs the verification itself.
  3. CA mismatch that presents as “no cert” - The client sends a certificate, but it was issued by a CA that isn’t in the server’s ssl_client_certificate bundle. Depending on configuration this can surface as a verification failure (495) rather than 496, but a misconfigured or truncated trust bundle — or ssl_verify_depth too shallow to reach the client cert’s issuer — is a frequent reason a cert you did send doesn’t count.

Diagnose with DechoNet

  • SSL Check reads the server’s TLS handshake and certificate from the outside — with no client certificate of its own. If the endpoint enforces mTLS for everyone, DechoNet’s cert-less probe is refused too, which confirms the requirement lives on the server and your fix is client-side (present a cert). It also confirms the server’s own certificate and chain are healthy, so you can rule out a server-cert problem and focus on the client-auth layer.
  • HTTP Check confirms the endpoint returns 400 / No required SSL certificate was sent to a plain request and captures the response, and — by testing from outside your network — tells you whether the requirement is global to the host or scoped to a path a proxy in your own path is mangling.

Resolution Checklist

  • Confirm the endpoint really wants a client cert. From a machine with the right certificate: curl -v --cert client.crt --key client.key https://YOUR_HOST/PATH. If that succeeds and a cert-less curl returns the 496/400, the requirement is real and client-side.
  • If you’re the client, present the certificate. In curl use --cert/--key; in a browser import the client certificate (PKCS#12) into the keystore and pick it at the prompt; in application code attach the cert and private key to the TLS/HTTP client’s context. Make sure the key matches the cert and the file paths resolve.
  • If you own the server, check the trust bundle. ssl_client_certificate must contain the full issuing chain for the client certs you accept, and ssl_verify_depth must be deep enough to reach the issuer. A truncated bundle turns a valid client cert into a rejection.
  • If there’s a proxy in front, verify it forwards the certificate. A terminating nginx/LB must pass the client cert to any backend that re-verifies it (forward $ssl_client_escaped_cert and $ssl_client_verify, or switch that hop to TLS pass-through). This is the usual reason “it works direct but not through the proxy.”
  • For intermittent 496 on reused connections, stop the certificate from being lost on resumption: keep ssl_verify_client at the server level rather than gating inside a location with if, and if the pattern tracks with keepalive, test with connection reuse disabled to confirm the diagnosis before tuning.

When to Escalate

  • If a cert-authenticated curl succeeds but your application still gets 496, the gap is in how your client builds its TLS context — the cert isn’t being attached to the outbound request. That’s an application/SDK configuration fix, not a server change.
  • If the certificate is correct and direct calls work but calls through your infrastructure fail, the intermediary is stripping the cert. Hand it to whoever owns that proxy/load balancer with the direct-vs-proxied evidence; they need to forward the client certificate or move verification.
  • If 496 appears only intermittently and only under load or long-lived connections, treat it as a connection-reuse behavior between your client stack and the mTLS endpoint, not a certificate defect — there’s nothing to reissue.

Related Tools

Related Guides

Share this guide

[Ad] Guide Detail Inline
← Back to All Guides