Short answer: openssl s_client -connect HOST:443 -servername HOST -brief checks a live TLS connection from the outside, and openssl x509 -in cert.pem -noout -text reads a certificate file you already have. Everything below was run against OpenSSL 3.0.22 on Debian 12; a few flags differ on OpenSSL 1.1.1 (Ubuntu 20.04 and Debian 11), noted where it matters.
| Task | Command |
|---|---|
| Check a live server's certificate | openssl s_client -connect example.com:443 -servername example.com -brief </dev/null |
| Expiry date of a .pem file | openssl x509 -in cert.pem -noout -dates |
| Full details of a .pem file | openssl x509 -in cert.pem -noout -text |
| Generate a self-signed cert | openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes |
| Generate a CSR for a CA | openssl req -new -newkey rsa:2048 -keyout key.pem -out request.csr -nodes |
| Random hex string | openssl rand -hex 32 |
| Random base64 string | openssl rand -base64 32 |
| SHA-256 of a file | openssl dgst -sha256 file.iso |
| Decode a base64 blob | openssl base64 -d -in file.b64 -out file.bin |
| Check a private key matches a cert | openssl x509 -noout -modulus -in cert.pem | openssl md5 |
| List supported TLS ciphers | openssl ciphers -v |
| OpenSSL version | openssl version |
Checking a live server: openssl s_client
openssl s_client -connect example.com:443 -servername example.com -brief </dev/nullCONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Peer certificate: CN = example.com
Hash used: SHA256
Signature type: ECDSA
Verification: OK
Server Temp Key: X25519, 253 bits
DONE-servernamesends SNI, which almost every host needs to pick the right certificate when several sites share one IP; leaving it out can connect you to the server’s default certificate instead of the one you meant to check.-briefis the short summary above; without it,s_clientprints the entire certificate chain and the handshake in full, useful when-briefis not enough.</dev/nullcloses standard input right away, so the command exits on its own instead of waiting at an open connection where you could otherwise type raw HTTP by hand.
-brief needs OpenSSL 1.1.1 or newer; every current distribution has it. The error line it prints when something is actually wrong, and what each one means, is covered in SSL handshake failed. The SSL checker runs the same check from a browser, with the full chain and expiry date laid out, when a terminal is not handy.
Other than HTTPS: SMTP, IMAP and STARTTLS
openssl s_client -connect mail.example.com:587 -starttls smtp
openssl s_client -connect mail.example.com:993Port 993 (IMAPS) is TLS from the first byte, same as 443; port 587 (SMTP submission) starts in plain text and only switches to TLS after a STARTTLS command, which is what -starttls smtp sends for you. Using the wrong one against the wrong port either hangs or fails at once. IMAP and POP3 ports lists which ones expect which mode.
Reading a certificate file: openssl x509
openssl x509 -in cert.pem -noout -datesnotBefore=Oct 14 09:00:00 2026 GMT
notAfter=Jan 12 09:00:00 2027 GMTopenssl x509 -in cert.pem -noout -text-noout stops it printing the raw base64 PEM block again underneath, which you almost never want to look at. The names a certificate actually covers are the Subject Alternative Names, not the subject line, which browsers stopped trusting on its own years ago:
openssl x509 -in cert.pem -noout -ext subjectAltNameX509v3 Subject Alternative Name:
DNS:example.com, DNS:www.example.comFingerprint and serial
openssl x509 -in cert.pem -noout -fingerprint -sha256
openssl x509 -in cert.pem -noout -serialCreating a CSR and a self-signed certificate
A certificate authority needs a Certificate Signing Request, not a finished certificate; a self-signed one is for internal tools and local development, since browsers reject it without you adding it as trusted yourself.
openssl req -new -newkey rsa:2048 -keyout key.pem -out request.csr -nodes \
-subj "/CN=example.com"openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes \
-subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com"-nodes(“no DES”) writes the private key without a passphrase — needed for a service that starts unattended, since otherwise it would ask for the password on every start.-newkey rsa:2048generates a new key in the same step; useec -pkeyopt ec_paramgen_curve:prime256v1instead ofrsa:2048for a smaller, faster Ed25519-era alternative where the client side supports it.-addext "subjectAltName=…"is the part people forget: without it, modern browsers and curl reject the certificate even though the subject line looks right, because there is no SAN at all.
For a real public site, Certbot and Let’s Encrypt do this whole exchange with a real CA for free — see nginx reverse proxy with SSL. openssl by hand is for internal services, a mail server’s own TLS, or testing.
Does this private key match that certificate?
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5Identical output means the key and certificate are a pair. This is the quickest way to find out whether an nginx config is pointing at mismatched files from two different certificate renewals.
openssl rand: random strings for scripts and secrets
openssl rand -hex 32 # 64 hex characters, often used as an API key or token
openssl rand -base64 32 # shorter, mixed-case, used for passwords and secrets
openssl rand -hex 16 > /etc/myapp/secret.keyBoth draw from the same cryptographically secure source as /dev/urandom; the only difference is the encoding. -hex doubles the byte count in its output length (16 bytes become 32 characters), -base64 is about 4/3 the byte count.
Checksums and signatures: openssl dgst
openssl dgst -sha256 ubuntu-24.04.iso
openssl dgst -sha256 -verify pubkey.pem -signature file.sig file.tar.gzThe first line is the same job as sha256sum, just from openssl directly, useful on a system that has openssl but not the coreutils checksum tools (some minimal containers). The second verifies a detached signature against a public key, which is how you check a download’s signature file rather than only its checksum.
Encoding and decoding: openssl base64
openssl base64 -in file.bin -out file.b64 # encode
openssl base64 -d -in file.b64 -out file.bin # decode
echo -n "hello" | openssl base64 # encode a short stringPlain base64 (the separate coreutils command) does the same job and is on every system too; openssl’s version is handy when you are already piping through openssl for something else in the same line.
What TLS versions and ciphers does this build support?
openssl version
openssl ciphers -v | head -20openssl version matters more than it looks: OpenSSL 1.1.1 reached end of life in September 2023 and gets no security fixes anywhere, so a package copied by hand from an old release should be replaced with one linked against OpenSSL 3, or the distribution’s own compatibility package where one exists (Rocky and AlmaLinux 9 ship compat-openssl11 as a stopgap for software that still needs it).
Common openssl error lines
| Line | Meaning |
|---|---|
| connect: Connection refused | Nothing is listening on that port — wrong port, or the service is down |
| errno=110 (Connection timed out) | No answer at all: a firewall between you and the server, most likely |
00…:SSL routines:…:wrong version number | Plain HTTP answered on a port you asked for TLS on — see SSL_ERROR_RX_RECORD_TOO_LONG |
| Verify return code: 10 (certificate has expired) | Exactly what it says; renew it |
| Verify return code: 20 (unable to get local issuer certificate) | The server sent its certificate but not the intermediate — a chain problem, not an expiry |
| Verify return code: 21 (unable to verify the first certificate) | Same chain problem, OpenSSL's older wording for it |
The “wrong version number” case is covered end to end, with the matching browser error text, in SSL_ERROR_RX_RECORD_TOO_LONG.
Frequently asked
How do I check a website's SSL certificate from the command line?
openssl s_client -connect example.com:443 -servername example.com -brief </dev/null. -servername sends SNI, which most hosts need to return the right certificate when several sites share one IP; without it you may see the server's default certificate instead of the one you meant to check.
How do I check when a certificate file expires?
openssl x509 -in cert.pem -noout -dates prints notBefore and notAfter. For every other detail — issuer, the Subject Alternative Names a certificate actually covers, the fingerprint — openssl x509 -in cert.pem -noout -text prints the lot.
How do I generate a self-signed certificate with openssl?
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com". The -addext line matters: without a Subject Alternative Name, current browsers and curl reject the certificate even if the subject line looks correct.
How do I generate a random string with openssl for a secret or API key?
openssl rand -hex 32 for a 64-character hex string, or openssl rand -base64 32 for a shorter mixed-case one. Both draw from the same cryptographically secure source; only the encoding differs.