The Error
Nginx refuses to start or reload, and you see this in the logs:
nginx: [emerg] cannot load certificate "/etc/nginx/ssl/cert.pem": BIO_new_file() failed (SSL: error:02001002:system library:fopen:No such file or directory)
The path in the error matches whatever you have in ssl_certificate inside your Nginx config. The underlying OpenSSL call BIO_new_file() failed. That usually means one of three things: the file doesn't exist at that path, Nginx can't read it, or the path is simply wrong.
Root Causes
- The certificate file simply doesn't exist at the configured path
- The path in
nginx.confhas a typo or points to the wrong location - The file exists but is owned by root and not readable by the
www-data/nginxuser - A symlink pointing to the cert is broken (common after Let's Encrypt renewal)
- The cert was stored on a mounted volume that isn't mounted yet at boot time
Fix 1: Confirm the File Actually Exists
Start simple โ does the file actually exist at that path?
ls -la /etc/nginx/ssl/cert.pem
If that returns No such file or directory, the file is missing. Check where your certificate actually lives:
# Let's Encrypt certs are here:
ls -la /etc/letsencrypt/live/yourdomain.com/
# Certbot / acme.sh typical output:
# cert.pem chain.pem fullchain.pem privkey.pem
Using Let's Encrypt? Point ssl_certificate to fullchain.pem, not cert.pem. Nginx needs the full chain โ just the leaf cert will fail TLS handshakes with most clients. The private key goes to privkey.pem:
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
Fix 2: Correct the Path in Your Nginx Config
Scan all your Nginx configs for the ssl_certificate directive:
grep -r 'ssl_certificate' /etc/nginx/
Then update the paths to match where your cert actually lives. Example for a custom cert stored in /etc/nginx/ssl/:
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# ... rest of config
}
After editing, always validate before reloading:
nginx -t
Fix 3: Create the SSL Directory and Copy Your Cert
Got a certificate from a commercial CA (say, they emailed you a .crt file)? Put it in the right place:
# Create the directory if missing
mkdir -p /etc/nginx/ssl
# Copy your certificate files
cp yourdomain.crt /etc/nginx/ssl/cert.pem
cp yourdomain.key /etc/nginx/ssl/privkey.pem
# Set restrictive permissions
chmod 600 /etc/nginx/ssl/privkey.pem
chmod 644 /etc/nginx/ssl/cert.pem
chown root:root /etc/nginx/ssl/privkey.pem
Fix 4: Check for a Broken Symlink (Let's Encrypt)
Let's Encrypt stores live certs as symlinks into /etc/letsencrypt/archive/. A failed renewal can leave the symlink pointing to a file that no longer exists:
# Check if symlinks are valid
ls -la /etc/letsencrypt/live/yourdomain.com/
# A broken symlink looks like:
# cert.pem -> ../../archive/yourdomain.com/cert3.pem (file doesn't exist)
# Verify the archive files exist
ls -la /etc/letsencrypt/archive/yourdomain.com/
If the archive files are gone, force a renewal:
certbot renew --force-renewal -d yourdomain.com
Or if you're using acme.sh:
acme.sh --renew -d yourdomain.com --force
Fix 5: Fix File Permissions
The file is there โ Nginx just can't open it. Find out which user the Nginx worker runs as:
ps aux | grep nginx | grep -v grep
# Look for the worker process user (www-data, nginx, or nobody)
Then test whether that user can actually read the cert:
# For Ubuntu/Debian (www-data user)
sudo -u www-data cat /etc/nginx/ssl/cert.pem
# For CentOS/RHEL (nginx user)
sudo -u nginx cat /etc/nginx/ssl/cert.pem
If access is denied, adjust permissions. Certs can be world-readable; private keys should not be:
chmod 644 /etc/nginx/ssl/cert.pem
chmod 640 /etc/nginx/ssl/privkey.pem
chown root:www-data /etc/nginx/ssl/privkey.pem
For Let's Encrypt, Nginx also needs execute permission on the live/ and archive/ directories to follow the symlinks. Without it, the traversal fails silently:
chmod 755 /etc/letsencrypt/live/
chmod 755 /etc/letsencrypt/archive/
chmod 755 /etc/letsencrypt/archive/yourdomain.com/
Verify the Fix
Before touching Nginx, run the config test:
nginx -t
# Expected output:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
Once it passes, reload (or restart if Nginx was stopped entirely):
# Reload running instance
systemctl reload nginx
# Or restart if it was stopped
systemctl restart nginx
# Confirm it's running
systemctl status nginx
Now confirm HTTPS actually works end-to-end:
curl -I https://yourdomain.com
# Should return HTTP/2 200 (or 301/302), not a connection error
# Check certificate details
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com < /dev/null 2>&1 | grep -E 'subject|issuer|expire'
Prevention
- Use absolute paths in
ssl_certificateโ never relative paths. - Hook into cert renewal: drop a script containing
nginx -t && systemctl reload nginxinto/etc/letsencrypt/renewal-hooks/deploy/so Nginx reloads automatically after every successful renewal. - Catch renewal failures early: run
certbot renew --dry-runin a weekly cron job โ it simulates renewal without replacing anything, so you'll know about failures before they cause downtime. - Avoid storing certs on external mounts: if you must, add
RequiresMountsFor=to the Nginx systemd unit so it waits for the mount before starting.

