Creating a self-signed server certificate can be a bit daunting, but with OpenSSL, it’s a breeze. Every developer eventually meets the moment when they need TLS for a server that no public CA will bless: a staging environment, an internal service, a local replica of prod. That is precisely when self-signed certificates earn their keep, and doing it properly means building a little Certificate Authority of your own instead of generating a bare certificate.
Follow this step-by-step guide to create your own self-signed server certificate, using the CA pattern so that machines which trust your CA will automatically trust every certificate it signs.
Step 1: 🏛️ Create a Certificate Authority (CA)
First, we need to create a Certificate Authority (CA). The CA is responsible for signing the server certificate. Trust flows from here: if a client trusts the CA, it trusts everything the CA signs.
openssl req -x509 -newkey rsa:4096 -days 3650 -noenc -sha256 -utf8 -keyout ca-key.pem -out ca-cert.pem -subj \
"/C=US/ST=Sample Region/L=Sample City/O=Sample Org/OU=Certificate Authority/CN=ca.sample.com/[email protected]"
This command generates a new private key (ca-key.pem) and a self-signed certificate (ca-cert.pem) valid for 10 years. The -noenc flag keeps the key unencrypted, which is fine for a throwaway internal CA and a pain in production, so plan accordingly.
Step 2: 🔍 Output the X.509 Certificate
To verify the details of the CA certificate, use the following command:
openssl x509 -in ca-cert.pem -noout -text
This command displays the certificate details in a human-readable format. It is the best first sanity check: if the dates, subject, and key algorithm look right, the CA is healthy.
Step 3: 📝 Create a Server Certificate Signing Request (CSR)
Next, we need to create a Certificate Signing Request (CSR) for the server certificate. This is the “application form” the server submits to the CA.
openssl req -newkey rsa:4096 -noenc -utf8 -sha256 -keyout server-key.pem -out server-csr.pem -subj \
"/C=US/ST=Sample Region/L=Sample City/O=Sample Org/OU=Certificate Authority/CN=server.sample.com/[email protected]"
This command generates a new private key (server-key.pem) and a CSR (server-csr.pem). Note the CN this time: it belongs to the server’s hostname, since this is the identity the certificate will carry.
Step 4: 🏷️ Add Subject Alternative Name (SAN)
To include Subject Alternative Names (SAN) in the server certificate, use the following command:
openssl x509 -req -in server-csr.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out sample-cert.pem -extfile \
&<(printf "extendedKeyUsage=serverAuth\nsubjectAltName=DNS:server.sample.com,DNS:localhost,IP:[IP_ADDRESS]")
This command signs the CSR with the CA certificate and includes the SANs server.sample.com, localhost, and [IP_ADDRESS]. This is the step people used to skip, and modern browsers and tools will reject certificates without a matching SAN, so list every hostname and IP you plan to connect through.
Step 5: 🧐 Output the Server X.509 Certificate
To verify the details of the server certificate, use the following command:
openssl x509 -in sample-cert.pem -noout -text
This command displays the server certificate details in a human-readable format. Look for the SAN extension and the issuer line; both should match your intent.
Step 6: 🔗 Verify the Certificate Chain
Finally, verify the certificate chain to ensure that the server certificate is correctly signed by the CA:
openssl verify -CAfile ca-cert.pem sample-cert.pem
This command verifies that the server certificate (sample-cert.pem) is signed by the CA certificate (ca-cert.pem). If it prints OK, the chain is valid, and any client that trusts ca-cert.pem will accept sample-cert.pem.
And there you have it! You’ve successfully created a self-signed server certificate using OpenSSL.
🧠 Final Thoughts
If you have any questions or need further assistance, feel free to ask. A small piece of advice before you start: keep the CA key somewhere safe, because losing it means every certificate it ever signed becomes unverifiable. And if your setup has more than a couple of hosts, consider keeping the -extfile content in a real config file instead of a process substitution, so you can reuse it cleanly.
Self-signed certificates will never be for the public internet, but for staging, internal services, and testing, they are the difference between “no TLS at all” and a properly encrypted setup that your future self will appreciate. Happy coding! 😊