In an ordinary HTTPS connection the server proves its identity and the visitor proves theirs afterwards, with a password. A client certificate moves the visitor's half into the connection itself.
The browser presents a certificate while connecting. Without one, the request is refused before any page is served.
Why that is different from a password
There is no login form, so there is nothing to attack. Automated login attempts stop entirely, because they never reach the application: they are refused at the connection.
And there is nothing to phish. A convincing fake login page captures nothing, because the credential is not something a person can be persuaded to type.
That is a genuinely different security property from any password arrangement, including two-factor.
What it costs
Administration, and it is the whole reason this is not used more.
Every person needs a certificate issued. It must be installed on every device they use: laptop, phone, second laptop. It must be renewed before it expires, or they are locked out with no self-service route. And it must be revoked when they leave.
A lost device means reissuing rather than resetting, which is a conversation in place of a form.
Where it fits
A small fixed group protecting something specific: an administrative area, an internal tool, a staging site, an API with a handful of known callers.
It fits customer logins very badly. Any arrangement requiring the customer to install something before they can use your site will lose customers, and it should not be attempted for a public audience.
Setting it up, in outline
You need your own certificate authority. A key and certificate you create, used to sign each person's certificate.
openssl genrsa -out ca.key 4096 openssl req -x509 -new -key ca.key -sha256 -days 3650 -out ca.crt
Then per person:
openssl genrsa -out alice.key 2048 openssl req -new -key alice.key -out alice.csr openssl x509 -req -in alice.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out alice.crt openssl pkcs12 -export -out alice.p12 -inkey alice.key -in alice.crt -certfile ca.crt
The .p12 file is what they install. It is protected by a passphrase you set, send that separately from the file, and treat it as a credential, because it is one.
Generating a CSR and managing private keys walks through handling these files properly.
Requiring it
SSLVerifyClient require SSLVerifyDepth 1 SSLCACertificateFile /path/to/ca.crt
Scoped to a directory or a virtual host instead of the whole site. Requiring it site-wide makes the public site unreachable, which is occasionally what you want for a staging server and rarely otherwise.
The ca.key must be kept somewhere safe and not on the web server. Anyone with it can issue certificates your server accepts.
Revocation is the part people skip
A certificate remains valid until it expires. Removing someone's access means publishing a revocation list and configuring the server to check it: otherwise a departed employee's certificate keeps working for the rest of its year.
The simpler alternative for very small groups: issue short-lived certificates, ninety days, and simply stop renewing for someone who leaves. Less elegant, considerably less to maintain, and it fails safely.
Keep a normal login behind it
A client certificate proves the connection came from a device you issued to. It does not identify a person, and it does not decide what they may do.
Keep the application's own authentication in place behind it. The certificate is a gate that removes the internet from your login page; it is not a replacement for knowing who is logged in.
For most sites, that gate is better achieved with a directory password or an address restriction, which cost nothing to administer. Password protecting a directory goes into the ordinary version, and it is the right answer far more often than this one.
Check what the server is actually asking for
A configuration that requests a certificate and does not require one produces a setup that appears to work and protects nothing.
curl -sI https://secure.example.com/ | head -1 curl -sI --cert client.pem --key client.key https://secure.example.com/ | head -1 echo | openssl s_client -connect secure.example.com:443 2>/dev/null | grep -A3 'Acceptable client certificate'
The first request, without a certificate, must be refused. If it succeeds, the requirement is set to optional and any visitor reaches the resource.
The third command shows which authorities the server will accept certificates from. An empty list means it accepts any certificate it can verify, which is broader than most people intend.
Issuing and tracking the certificates
The administrative work is the real cost, and it starts before the first certificate is issued.
openssl genrsa -out client.key 2048 openssl req -new -key client.key -out client.csr -subj "/[email protected]" openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out client.crt openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12
The exported bundle is what a person installs. It contains the private key, so it has to be delivered securely and with a password, and it must not be emailed as an attachment.
Record who received which certificate and when it expires. Without that record, revoking access for one person means either reissuing for everybody or not revoking at all.
Plan the failure modes before deploying it
This control fails closed, which is the point and also the risk.
A person with a new laptop cannot reach the resource until somebody issues and delivers a certificate. A certificate that expires locks its holder out with a message most people cannot interpret. And a browser that does not present the certificate automatically produces a refusal that reads as a server fault.
openssl x509 -in client.crt -noout -enddate -subject
So keep a second route in, watch the expiry dates rather than waiting for reports, and be certain the person who administers this is not the only one who can issue a replacement. Password protecting a directory covers the simpler control that fits most cases.
Decide what happens when the certificate is lost
Every recovery path for this control is manual, which is worth arranging before it is needed.
A person with a new machine cannot reach the resource until somebody issues and delivers a replacement. If the only person who can issue one is the person locked out, the recovery is a rebuild.
openssl x509 -in client.crt -noout -enddate -subject ls -la ca.key
Keep the signing key somewhere at least two people can reach, backed up, and separate from the server it protects. Losing it means reissuing for everybody rather than for one person.
Combine it with something the certificate cannot know
A certificate proves the machine holds a file. It does not prove who is using the machine.
A borrowed or stolen laptop presents the certificate exactly as its owner would, and the server has no way to tell. That is the limitation to design around rather than to ignore.
Keeping an ordinary login behind the certificate handles it: the certificate decides which machines may reach the resource at all, and the login decides who is using it. The two answer different questions, and together they cover what neither does alone.