496 SSL Certificate Required
When a request arrives without the required client certificate for mutual TLS, nginx responds with 496 SSL Certificate Required.
Usage
The 496 SSL Certificate Required status code indicates the client sent an HTTP request without an SSL certificate, yet the server requires one for the requested resource. Resolving this error requires resubmitting the request with a valid client certificate attached.
Example
A client sends a request to a resource requiring mutual TLS, but provides no client certificate. The nginx server records the failure as 496 SSL Certificate Required and, in the default configuration, answers the client with a 400 response carrying the built-in error page.
Request
GET /secure/internal HTTP/1.1
Host: www.example.re
Response
HTTP/1.1 400 Bad Request
Server: nginx
Content-Type: text/html
Connection: close
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx</center>
</body>
</html>
The nginx error log records the missing certificate:
client sent no required SSL certificate
Requiring a client certificate
496 arises only under ssl_verify_client on. The
optional mode verifies a certificate when one
arrives and accepts a request without one, so a
missing certificate never produces the code there.
ssl_client_certificate /etc/nginx/ssl/client-ca.crt;
ssl_verify_client on;
Switching between the two modes is the usual fix
rather than a change on the client side. A public
endpoint requiring certificates from a subset of
clients wants optional together with an application
level check on $ssl_client_verify, and an endpoint
serving only certificate holders wants on.
if ($ssl_client_verify != SUCCESS) {
return 403;
}
The handshake completes before the check runs, so the absence surfaces as an HTTP response rather than a connection failure, and the client sees 400 unless an error page changes the outcome.
error_page 496 =400 /errors/cert-missing.html;
location = /errors/cert-missing.html {
root /var/www;
internal;
}
Place the directive at server or http level. No
location is selected at the point the condition
arises, leaving a location block unmatched.
An error_page line anywhere in a server block
replaces the whole inherited set from http level
rather than adding to the set, so a lone
error_page 404 in a server block silently removes
an inherited certificate handler.
How to fix
The server expects a client certificate for mutual TLS (mTLS) and the client sent none. The fix depends on whether the client or server configuration needs to change.
Client-side fix
Install the required client certificate in the
browser, operating system trust store, or HTTP
client library. For curl, pass the certificate
and key:
curl --cert client.pem --key client-key.pem \
https://www.example.re/secure/internal
For browser access, import the .p12 or .pfx
certificate bundle into the browser's certificate
manager. The browser presents the certificate
automatically during the TLS handshake when the
server requests one.
Server-side fix
Check the ssl_verify_client directive. Three
values control behavior:
# Reject if no cert (strict mTLS)
ssl_verify_client on;
# Accept with or without cert
ssl_verify_client optional;
# Accept without cert, skip CA verification
ssl_verify_client optional_no_ca;
Setting the value to optional allows requests
without certificates to proceed while still
verifying certificates when presented. This is
useful for endpoints serving both
authenticated and anonymous
traffic.
Verify the ssl_client_certificate directive
points to a valid CA bundle file containing all
trusted root and intermediate certificates:
ssl_client_certificate /etc/nginx/ca-chain.pem;
ssl_verify_client on;
Use error_page 496 to serve a helpful error
page or redirect to a login page
instead of the
default nginx error:
error_page 496 =301 https://$host/login;
See also
- 400
- Google: HTTP status codes and network errors
- 495
- nginx ssl_module Error Processing
- HTTP status codes