Fix Nginx "Cannot Load Certificate" for a Django Site

Fix Nginx emerg errors such as cannot load certificate BIO_new_file() failed, no such file, or key values mismatch: check certificate paths, Certbot live symlinks, file permissions, PEM formats, and that the key matches the certificate.

Problem statement

After moving servers, restoring a backup, or editing TLS settings, nginx -t fails:

nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem":
BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:
calling fopen(/etc/letsencrypt/live/example.com/fullchain.pem, r) ...)

or:

nginx: [emerg] SSL_CTX_use_PrivateKey("/etc/ssl/private/example.key") failed
(SSL: error:05800074:x509 certificate routines::key values mismatch)

Nginx reads certificates and keys when it loads the configuration. If a file is missing, unreadable, in the wrong format, or the key does not belong to the certificate, Nginx will not start or reload. Your Django app is fine; the HTTPS front door is closed.

Why Nginx will not load TLSConfig pointsto cert and keyNginx reads filesat config loadMissing or wrongpath, perms, pairemerg errorreload refusedNginx validates certificate files at load time, before any request reaches Django.
  1. Check the path exists: sudo ls -l /etc/letsencrypt/live/example.com/.
  2. Certbot's live/ files are symlinks into archive/; copy both directories (with links) when moving servers, or reissue.
  3. Use fullchain.pem for ssl_certificate and privkey.pem for ssl_certificate_key.
  4. Check the key matches: compare openssl x509 -noout -pubkey -in cert.pem with openssl pkey -pubout -in key.pem.
  5. Make sure files are readable by the user Nginx starts as (root reads them, then drops privileges).

Step-by-step solution

1. Find which file and which error

nginx -t names the file and includes the OpenSSL reason. No such file or directory is a path problem. key values mismatch is a pairing problem. PEM_read_bio_X509_AUX ... no start line is a format problem.

sudo ls -l /etc/letsencrypt/live/example.com/

Each file in live/ should be a symlink to a numbered file in archive/example.com/. Copying only live/ with cp or rsync without -a, or restoring from a backup that did not preserve links, leaves broken symlinks. Options:

  • Copy the whole /etc/letsencrypt directory with sudo rsync -a or tar from the old server.
  • Or issue a new certificate on the new server: sudo certbot --nginx -d example.com -d www.example.com.

When issuing fresh, temporarily comment out the TLS server block or Certbot's own Nginx test will fail on the missing files.

read the OpenSSL reasonReason in errorCause and fixNo such file or directorywrong path or brokenlive symlink; reissuekey values mismatchkey from another cert;use matching pairno start linenot PEM or wrong file; convert or swapPermission deniedunreadable at load; fix owner and modeThe text after BIO_new_file or SSL_CTX tells you which case it isdjango-deployment.com
Run nginx -t after every TLS change to see the reason before a reload.

3. Use the right files in the right directives

server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

fullchain.pem includes the intermediate certificate browsers need. Using cert.pem loads, but some clients then fail with "unable to verify the first certificate". Putting the key in ssl_certificate gives "no start line".

4. Check that key and certificate match

openssl x509 -noout -pubkey -in fullchain.pem | sha256sum
openssl pkey -pubout -in privkey.pem | sha256sum

The two hashes must be identical. Mismatches happen when a certificate is reissued with a new key and only one file is updated, or when files from two domains get mixed up.

5. Convert formats from commercial CAs

Certificates bought from a CA may come as .crt, .cer, .pfx, or a ZIP of separate files. Nginx needs PEM: the server certificate followed by intermediates in one file, and an unencrypted PEM key:

cat example_com.crt intermediate.crt > fullchain.pem
openssl pkcs12 -in example.pfx -nocerts -nodes -out privkey.pem      # from PFX

An encrypted key (-----BEGIN ENCRYPTED PRIVATE KEY-----) makes Nginx prompt for a passphrase at startup, which fails under systemd; remove the passphrase or use ssl_password_file.

Certbot file namesContainsNginx directivefullchain.pemcert + intermediatesssl_certificateprivkey.pemprivate keyssl_certificate_keycert.pemcert only!avoid alonechain.pemintermediatesssl_trusted_certificatefullchain and privkey are the pair Nginx needsdjango-deployment.com
chain.pem is only needed for OCSP stapling verification.

Explanation

Nginx's master process runs as root and loads all certificates and keys while parsing the configuration. It asks OpenSSL to open each file, parse it, and verify that the private key corresponds to the certificate's public key. Any failure is fatal for the configuration, so nginx -t fails and a reload is refused, leaving the previous configuration running if Nginx was already up. A cold start or restart with a broken configuration leaves no HTTPS listener at all, which is why testing before restarting matters.

Edge cases / notes

  • Certbot renewals. Renewal changes the archive/ file numbers and updates the live/ symlinks; always reference live/, never archive/ files directly.
  • Docker. Mount /etc/letsencrypt as a whole directory; mounting single live/ files breaks when renewals change symlink targets.
  • Variables in paths. ssl_certificate accepts variables for per-host certificates, but then files load per handshake and errors appear in the error log instead of at startup.

Next steps

FAQ

Why does Nginx not start after copying /etc/letsencrypt?

The copy probably broke the symlinks in live/. Copy with rsync -a or tar, or reissue the certificate.

Should I use cert.pem or fullchain.pem?

fullchain.pem. It includes the intermediate certificates that clients need to verify your certificate.

How do I check that my key matches the certificate?

Compare the SHA-256 of the public key extracted from each file with openssl. They must be identical.

Does this affect my Django app?

Only through Nginx. Django keeps running, but HTTPS requests cannot reach it until Nginx loads the certificate.

Keep going