# Nginx SSL_CTX_use_PrivateKey failed: fix the private key

> Nginx cannot load the private key: a mismatched key, wrong format, passphrase or file permissions. The OpenSSL reason at the end of the line names the cause.

Source: https://ssl.sh/errors/nginx-ssl-ctx-use-privatekey-failed/
Last updated: 2026-11-10

**Nginx prints this, or the related `cannot load certificate key "...": PEM_read_bio_PrivateKey() failed`, when it cannot use the private key in `ssl_certificate_key`.** The text in parentheses after `failed` is the real reason: a mismatched key, a file that is not a PEM key, an encrypted key, or a file Nginx cannot read.

## Quick fix

The reason in parentheses decides the fix.

Validate the key and the certificate pair outside Nginx:

```bash
sudo openssl pkey -in /etc/nginx/ssl/example.com.key -noout && echo "key parses"
sudo openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -pubkey | openssl sha256
sudo openssl pkey -in /etc/nginx/ssl/example.com.key -pubout | openssl sha256
```

Then match the reason Nginx printed:

| Reason in the log | Meaning | Go to |
|---|---|---|
| `key values mismatch` | key belongs to another certificate | [key values mismatch](https://ssl.sh/errors/nginx-key-values-mismatch/) |
| `no start line`, `DECODER routines::unsupported` | not a PEM private key | Cause 2 |
| `bad decrypt`, `bad password read`, passphrase prompt | encrypted key | Cause 3 |
| `Permission denied`, `No such file or directory` | file access | Cause 4 |

Fix, then run `sudo nginx -t && sudo systemctl reload nginx`.

## Causes

### 1. The key does not match the certificate

A key that does not match the certificate is the most frequent reason behind `SSL_CTX_use_PrivateKey ... failed`; see [key values mismatch](https://ssl.sh/errors/nginx-key-values-mismatch/).

- **Confirm:** the two hashes above differ.
- **Fix:** see [Nginx key values mismatch](https://ssl.sh/errors/nginx-key-values-mismatch/).

### 2. The file is not a PEM private key

Nginx expects a PEM block such as `-----BEGIN PRIVATE KEY-----`, `BEGIN RSA PRIVATE KEY` or `BEGIN EC PRIVATE KEY`. DER files, PKCS#12 (`.pfx`/`.p12`) bundles, a certificate given as the key, or an empty file produce decoder errors such as `no start line` (OpenSSL 1.1.1) or `DECODER routines::unsupported` (OpenSSL 3).

- **Confirm:** `head -1 example.com.key` does not show a `BEGIN ... PRIVATE KEY` line, or `file example.com.key` reports binary data.
- **Fix:** convert:

```bash
# DER to PEM
openssl pkey -inform DER -in example.com.der -out example.com.key
# PKCS#12 to PEM key
openssl pkcs12 -in site.pfx -nocerts -nodes -out example.com.key
```

Also check for a UTF-8 byte-order mark or Windows line endings added by an editor (`dos2unix` removes them).

### 3. The key is encrypted

Keys created with a passphrase start with `BEGIN ENCRYPTED PRIVATE KEY` (or contain `Proc-Type: 4,ENCRYPTED`). Nginx then prompts for the passphrase on the console, and service managers fail with `bad decrypt` or `bad password read`.

- **Fix:** add the passphrase to a root-only file and reference it:

```nginx
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_password_file   /etc/nginx/ssl/example.com.pass;
```

or decrypt once with `openssl pkey -in enc.key -out example.com.key` and protect the result with `chmod 600`.

### 4. File permissions, paths or SELinux

- **Confirm:** the message contains `BIO_new_file() failed` with `Permission denied` or `No such file or directory`. On RHEL-family systems, `ls -Z` shows a label such as `user_home_t` for files moved from a home directory.
- **Fix:** correct the path, set `chown root:root` and `chmod 600`, and on SELinux systems restore labels with `sudo restorecon -Rv /etc/nginx/ssl`.

### 5. Unsupported key type for the OpenSSL build

Old OpenSSL builds cannot load newer key types, and some FIPS-mode builds reject small or non-approved keys.

- **Fix:** use an RSA 2048+ or ECDSA P-256/P-384 key, which every current build supports.

## Fixes by environment

On every system, Nginx needs a [PEM](https://ssl.sh/glossary/pem/) key that matches the certificate, that it can decrypt, and that the master process can read.

**Debian / Ubuntu:** keys usually live under `/etc/ssl/private/` (mode 710, group `ssl-cert`); keep Nginx's key there or under `/etc/nginx/ssl/`.

**RHEL / Rocky / AlmaLinux:** put keys under `/etc/pki/tls/private/` or `/etc/nginx/ssl/` and run `restorecon` after copying.

**Docker:** mount keys read-only and check the user the container starts as; official Nginx images start the master as root, but rootless variants may need the key readable by their user.

**Let's Encrypt:** reference `/etc/letsencrypt/live/<name>/privkey.pem`; Certbot writes unencrypted PEM keys with restrictive permissions.

## Don't do this

- **`chmod 644` or `777` on the key to "fix permissions".** Any local user or compromised web application could then copy the key.
- **Put the passphrase in the Nginx config or a world-readable file.** Use `ssl_password_file` with root-only permissions.
- **Store keys inside the web root.** A misconfigured location block can serve them to anyone.
