# Fetch a certificate from a live server

To fetch a server's certificate, run `openssl s_client -connect example.com:443 -servername example.com </dev/null`. The `-servername` flag sends SNI and is essential — without it a virtual-hosted server answers with its default certificate, so you inspect the wrong one. Add `-showcerts` to see the full chain the server sends, and pipe the output through `openssl x509` to decode it. The `</dev/null` closes stdin so the command exits instead of waiting for input.

Source: https://certguru.io/openssl/get-certificate-from-server/
Last updated: 2026-08-19
Commands verified against OpenSSL 3.5.7.

---

## The basic command

*Connect and show what the server presents*

```bash
openssl s_client -connect example.com:443 -servername example.com  **-servername is the flag people leave out**
>
> Modern hosting is virtual-hosted by default. Without SNI, the server has no idea which site you
>   are asking about and replies with whatever its default is — often a completely unrelated
>   certificate. Every command on this page includes it for that reason.

## Just the certificate, decoded

*Pipe the leaf certificate into x509*

```bash
openssl s_client -connect example.com:443 -servername example.com /dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
```

Expected output:

```
subject=CN = example.com
issuer=C = US, O = Example CA, CN = Example Intermediate CA
notBefore=Aug 16 00:00:00 2026 GMT
notAfter=Nov 14 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com
```

## Saving the certificate to a file

*Save just the server certificate*

```bash
openssl s_client -connect example.com:443 -servername example.com /dev/null \
  | openssl x509 -out example.com.crt
```

## Saving the whole chain

`-showcerts` prints every certificate the server sent. Extract them all with `sed`:

*Save the full chain as the server presents it*

```bash
openssl s_client -connect example.com:443 -servername example.com -showcerts /dev/null \
  | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
  > chain.pem
```

Then inspect what came back:

```bash
openssl crl2pkcs7 -nocrl -certfile chain.pem | openssl pkcs7 -print_certs -noout
```

## Reading the verification result

The last useful line of `s_client` output is the verdict:

*Just the verdict*

```bash
openssl s_client -connect example.com:443 -servername example.com /dev/null \
  | grep "Verify return code"
```

Expected output:

```
Verify return code: 0 (ok)
```

| Code | Meaning | Page |
| --- | --- | --- |
| `0 (ok)` | Chain validated against your trust store | — |
| `10` | Certificate has expired | [expiry](/openssl/check-certificate-expiry/) |
| `18` | Self-signed certificate | [self-signed vs CA-signed](/concepts/self-signed-vs-ca-signed/) |
| `19` | Self-signed certificate in chain | [that error](/errors/self-signed-certificate-in-chain/) |
| `20` | Unable to get local issuer certificate | [that error](/errors/unable-to-get-local-issuer-certificate/) |
| `21` | Unable to verify the first certificate | [incomplete chain](/errors/unable-to-get-local-issuer-certificate/) |

## Counting what the server sends

A quick way to spot a missing intermediate. A publicly trusted site should send at least two:

*How many certificates came back?*

```bash
openssl s_client -connect example.com:443 -servername example.com -showcerts /dev/null \
  | grep -c "BEGIN CERTIFICATE"
```

Expected output:

```
2
```

## Other protocols

*SMTP with STARTTLS*

```bash
openssl s_client -connect mail.example.com:587 -starttls smtp </dev/null
```

*IMAP, PostgreSQL, LDAP*

```bash
openssl s_client -connect mail.example.com:143 -starttls imap </dev/null
openssl s_client -connect db.example.com:5432 -starttls postgres </dev/null
openssl s_client -connect ldap.example.com:389 -starttls ldap </dev/null
```

## Pinning down the TLS version

Adding a version flag constrains the handshake, which is how you find out what a server still
accepts:

*Force a specific protocol version*

```bash
openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null
```

A handshake failure here is the expected result when the server has disabled that version — but on
OpenSSL 3 it can equally be your own client refusing to make the offer. Interrogating versions and
cipher suites properly is its own job: see
[check which TLS versions a server supports](/openssl/check-tls-versions-and-ciphers/).

## Checking a server that is not yet live

To test a certificate before DNS points at the new host, resolve elsewhere but keep SNI honest:

*Connect to an IP while presenting the real hostname*

```bash
openssl s_client -connect 203.0.113.10:443 -servername example.com </dev/null
```


## Frequently asked questions

### Why do I need </dev/null?

`s_client` opens an interactive session and waits on stdin so you can type an HTTP request by hand. Redirecting from /dev/null closes stdin immediately, so the command prints its output and exits — which is what you want in a script.

### What does -servername actually do?

It sends the Server Name Indication TLS extension, telling the server which hostname you are asking for. On shared hosting, omitting it means the server replies with its default certificate and you end up debugging a certificate nobody uses.

### How do I check a mail server or another protocol?

Use `-starttls`. For example `openssl s_client -connect mail.example.com:587 -starttls smtp`. It also supports imap, pop3, ftp, xmpp, ldap and postgres.

### Why does verification fail here but work in my browser?

Browsers repair incomplete chains by caching intermediates and fetching missing ones over HTTP. OpenSSL does neither, so it fails where a browser succeeds. The server is genuinely misconfigured — see that error.


## References

- [openssl-s_client(1)](https://docs.openssl.org/3.5/man1/openssl-s_client/) — -connect, -showcerts and -servername

## Related pages

- https://certguru.io/openssl/check-certificate-expiry/
- https://certguru.io/openssl/verify-certificate-chain/
- https://certguru.io/openssl/check-tls-versions-and-ciphers/
- https://certguru.io/errors/wrong-version-number/
- https://certguru.io/errors/unable-to-get-local-issuer-certificate/
- https://certguru.io/openssl/view-certificate/
- https://certguru.io/concepts/ocsp-vs-crl/
- https://certguru.io/concepts/certificate-chain/

--
CertGuru — https://certguru.io
