Create a self-signed localhost certificate with correct SANs
Generate a short-lived local TLS certificate, include every DNS/IP identity, protect the private key, establish trust deliberately and verify hostname plus chain.
A self-signed certificate is useful for isolated development and controlled laboratories. It provides encryption and proves possession of its private key, but clients will not trust it automatically. Modern hostname verification also depends on Subject Alternative Names (SANs), not merely a convenient CN=localhost.
Decide the exact identities before generating anything
If clients connect as localhost, api.test, 127.0.0.1 and ::1, each identity needs the correct SAN type. DNS names use DNS:; numeric addresses use IP:. A certificate for localhost does not match 127.0.0.1 unless both appear.
Generate a short-lived key and certificate
umask 077
openssl req -x509 -newkey rsa:3072 -sha256 -days 30 -noenc -keyout localhost.key -out localhost.crt -subj '/CN=localhost' -addext 'subjectAltName=DNS:localhost,DNS:api.test,IP:127.0.0.1,IP:::1' -addext 'basicConstraints=critical,CA:FALSE' -addext 'keyUsage=critical,digitalSignature,keyEncipherment' -addext 'extendedKeyUsage=serverAuth'
Current OpenSSL uses -noenc; older releases commonly show -nodes. An unencrypted service key permits unattended startup, so filesystem access becomes the primary protection. If the server supports an encrypted key through a secret manager or agent, use that operational model instead.
Inspect the artifact rather than trusting the command
openssl x509 -in localhost.crt -noout -subject -issuer -serial -dates -fingerprint -sha256
openssl x509 -in localhost.crt -noout -ext subjectAltName,basicConstraints,keyUsage,extendedKeyUsage
openssl pkey -in localhost.key -check -noout
A self-signed leaf has the same subject and issuer. Confirm the SANs, validity window, key usage and CA false constraint. Then confirm the public keys match:
openssl x509 -in localhost.crt -pubkey -noout |
openssl pkey -pubin -outform DER |
openssl dgst -sha256
openssl pkey -in localhost.key -pubout -outform DER |
openssl dgst -sha256
The two digests must be identical.
Protect the private key
chmod 0600 localhost.key
chmod 0644 localhost.crt
ls -l localhost.key localhost.crt
Keep the key out of source control, container images, tickets and chat. Add filename patterns to ignore rules, but remember that ignore rules do not remove a key already committed. If exposure is suspected, replace it and remove trust for the old certificate.
Configure the local server with separate certificate and key files
server {
listen 8443 ssl;
server_name localhost api.test;
ssl_certificate /etc/local-tls/localhost.crt;
ssl_certificate_key /etc/local-tls/localhost.key;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
sudo nginx -t
sudo systemctl reload nginx
Restrict the key so the service account can read it but unrelated users cannot. The exact group and deployment path should follow the service manager’s security model.
Test encryption without pretending trust
openssl s_client -connect 127.0.0.1:8443 -servername localhost -showcerts </dev/null
This shows the handshake and presented chain. A verification error is expected until the client is explicitly configured to trust the certificate or its issuing development CA.
Verify trust and hostname together
openssl s_client -connect 127.0.0.1:8443 -servername localhost -verify_hostname localhost -verify_return_error -CAfile localhost.crt </dev/null
curl --cacert localhost.crt --resolve localhost:8443:127.0.0.1 https://localhost:8443/health
curl -k disables verification and can hide the exact defect you are trying to fix. Use a scoped CA file or controlled local trust store instead.
Prefer a development CA for teams and multiple services
Trusting each self-signed leaf does not scale. A private development CA can be installed once on managed test devices and used to sign short-lived leaf certificates. Protect the CA key more strongly than any server key, keep it out of application hosts, mark CA constraints correctly, and never install a development root on production systems.
Diagnose the common failures
| Error | Cause | Repair |
|---|---|---|
| Certificate not trusted | No trust anchor configured | Use scoped CA file or managed local trust |
| Hostname mismatch | Connected name absent or wrong SAN type | Regenerate with correct DNS/IP SAN |
| Key values mismatch | Certificate paired with another private key | Compare public-key digests and deploy pair |
| Permission denied reading key | Service account lacks access | Correct owner/group and restrictive mode |
Works only with -k | Verification was disabled, not fixed | Test chain and hostname explicitly |
Know where self-signed stops being appropriate
For public services, use a certificate issued through the organization’s approved public or private PKI, with automated renewal and monitoring. A self-signed localhost certificate is not a shortcut around domain control, revocation, rotation or trust distribution.
TLS can encrypt a connection to the wrong peer. Verification—trust chain plus hostname—is what turns encryption into authenticated transport.
Sources: the solved Stack Overflow question (question by michelemarcon; accepted answer by Diego Woitasen, CC BY-SA), OpenSSL’s official req, x509, and X.509 extension documentation.
Primary source: Review the official reference ↗