NginxのSSLエラー「cannot load certificate」を起動・リロード時に修正する

intermediate Nginx2026-04-20| Ubuntu 20.04/22.04、Debian 11/12、CentOS 7/8、RHEL上のNginx 1.18以降 — SSL/TLSを使用するNginxが動作する任意のLinuxシステム

Error Message

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)
#nginx#ssl#tls#証明書#https

エラーの内容

Nginxが起動またはリロードを拒否し、ログに以下のメッセージが表示されます:

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)

エラーに表示されているパスは、Nginx設定ファイルのssl_certificateに記述したパスと一致しています。内部のOpenSSL呼び出しBIO_new_file()が失敗しており、原因として主に3つが考えられます:そのパスにファイルが存在しない、Nginxがファイルを読み取れない、またはパスそのものが誤っている、のいずれかです。

根本的な原因

  • 設定されたパスに証明書ファイルが存在しない
  • nginx.conf内のパスにタイポがあるか、誤った場所を指している
  • ファイルは存在するが、rootが所有しておりwww-data / nginxユーザーが読み取れない
  • 証明書を指しているシンボリックリンクが壊れている(Let's Encryptの更新後によく発生する)
  • 証明書がマウントされたボリュームに保存されており、起動時にまだマウントされていない

修正1:ファイルが実際に存在するか確認する

まずシンプルに確認しましょう — そのパスにファイルは実際に存在しますか?

ls -la /etc/nginx/ssl/cert.pem

No such file or directoryが返ってきた場合、ファイルが存在しません。証明書が実際にどこにあるか確認しましょう:

# Let's Encryptの証明書はここにあります:
ls -la /etc/letsencrypt/live/yourdomain.com/

# Certbot / acme.sh の典型的な出力:
# cert.pem  chain.pem  fullchain.pem  privkey.pem

Let's Encryptを使用している場合、ssl_certificateにはcert.pemではなくfullchain.pemを指定してください。Nginxはフルチェーンが必要であり、リーフ証明書だけでは多くのクライアントとのTLSハンドシェイクに失敗します。秘密鍵はprivkey.pemを指定します:

ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

修正2:Nginx設定ファイルのパスを修正する

すべてのNginx設定ファイルでssl_certificateディレクティブを検索します:

grep -r 'ssl_certificate' /etc/nginx/

次に、証明書が実際に存在する場所に合わせてパスを更新します。/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;

    # ... 残りの設定
}

編集後は、必ずリロード前に設定を検証してください:

nginx -t

修正3:SSLディレクトリを作成して証明書をコピーする

商業CAから証明書を取得した場合(例えば、.crtファイルがメールで送られてきた場合)、正しい場所に配置します:

# ディレクトリが存在しない場合は作成する
mkdir -p /etc/nginx/ssl

# 証明書ファイルをコピーする
cp yourdomain.crt /etc/nginx/ssl/cert.pem
cp yourdomain.key /etc/nginx/ssl/privkey.pem

# 制限的なパーミッションを設定する
chmod 600 /etc/nginx/ssl/privkey.pem
chmod 644 /etc/nginx/ssl/cert.pem
chown root:root /etc/nginx/ssl/privkey.pem

修正4:壊れたシンボリックリンクを確認する(Let's Encrypt)

Let's Encryptは有効な証明書を/etc/letsencrypt/archive/へのシンボリックリンクとして保存します。更新が失敗すると、存在しないファイルを指すシンボリックリンクが残ることがあります:

# シンボリックリンクが有効か確認する
ls -la /etc/letsencrypt/live/yourdomain.com/

# 壊れたシンボリックリンクはこのように見えます:
# cert.pem -> ../../archive/yourdomain.com/cert3.pem  (ファイルが存在しない)

# アーカイブファイルが存在するか確認する
ls -la /etc/letsencrypt/archive/yourdomain.com/

アーカイブファイルが消えている場合は、強制的に更新します:

certbot renew --force-renewal -d yourdomain.com

acme.shを使用している場合:

acme.sh --renew -d yourdomain.com --force

修正5:ファイルのパーミッションを修正する

ファイルは存在しているのに、Nginxが開けない場合があります。Nginxワーカーがどのユーザーで動作しているか確認します:

ps aux | grep nginx | grep -v grep
# ワーカープロセスのユーザー(www-data、nginx、またはnobody)を探してください

次に、そのユーザーが実際に証明書を読み取れるかテストします:

# Ubuntu/Debian の場合(www-dataユーザー)
sudo -u www-data cat /etc/nginx/ssl/cert.pem

# CentOS/RHEL の場合(nginxユーザー)
sudo -u nginx cat /etc/nginx/ssl/cert.pem

アクセスが拒否された場合は、パーミッションを調整します。証明書は全ユーザーが読み取れても構いませんが、秘密鍵は読み取れないようにすべきです:

chmod 644 /etc/nginx/ssl/cert.pem
chmod 640 /etc/nginx/ssl/privkey.pem
chown root:www-data /etc/nginx/ssl/privkey.pem

Let's Encryptの場合、Nginxはシンボリックリンクをたどるためにlive/およびarchive/ディレクトリへの実行権限も必要です。これがないと、ディレクトリトラバーサルが静かに失敗します:

chmod 755 /etc/letsencrypt/live/
chmod 755 /etc/letsencrypt/archive/
chmod 755 /etc/letsencrypt/archive/yourdomain.com/

修正の確認

Nginxを操作する前に、設定テストを実行します:

nginx -t
# 期待される出力:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

テストが通ったら、リロードします(Nginxが完全に停止していた場合は再起動します):

# 実行中のインスタンスをリロードする
systemctl reload nginx

# 停止していた場合は再起動する
systemctl restart nginx

# 動作を確認する
systemctl status nginx

次に、HTTPSがエンドツーエンドで正常に動作することを確認します:

curl -I https://yourdomain.com
# 接続エラーではなく、HTTP/2 200(または301/302)が返ってくるはずです

# 証明書の詳細を確認する
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com < /dev/null 2>&1 | grep -E 'subject|issuer|expire'

予防策

  • 絶対パスを使用するssl_certificateには相対パスではなく、必ず絶対パスを指定してください。
  • 証明書更新にフックを設定する/etc/letsencrypt/renewal-hooks/deploy/nginx -t && systemctl reload nginxを含むスクリプトを配置することで、更新が成功するたびにNginxが自動的にリロードされます。
  • 更新の失敗を早期に検知する:週次のcronジョブでcertbot renew --dry-runを実行しましょう — 実際には証明書を置き換えずに更新をシミュレートするため、ダウンタイムが発生する前に問題を把握できます。
  • 外部マウントへの証明書の保存を避ける:やむを得ない場合は、NginxのsystemdユニットファイルにNginx RequiresMountsFor=を追加して、起動前にマウントが完了するまで待機するようにしてください。

Related Error Notes