You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nginx配置文件中动态设置SSL证书密钥路径?

动态匹配Let's Encrypt证书实现多域名SSL服务

问题描述

我在/etc/letsencrypt/live/目录下有多个域名文件夹,每个文件夹包含对应的证书文件(fullchain.pem)和密钥文件(privkey.pem)。希望Nginx能为该目录下的任意域名提供SSL服务,并且后续会通过程序自动添加新域名。

当前Nginx配置如下:

server {
    listen 443 ssl;
    server_name default_server;

     map "$host" $domain_name { ~(.*)\.(.*)\.(.*)$ $2.$3; }

    ssl_certificate /etc/letsencrypt/live/$domain_name/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$domain_name/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 20M;
...
}

请求域名时出现错误:

No such file or directory:fopen('/etc/letsencrypt/live/default_server/fullchain.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file) while SSL handshaking

可见$domain_name变量未正确解析,被替换成了default_server,请问该如何实现需求?

解决方案

出现这个问题的核心原因是Nginx在SSL握手阶段无法使用$host变量——SSL握手发生在HTTP请求之前,此时$host还未被解析,Nginx会直接使用server_name的值作为 fallback,也就是default_server,导致证书路径错误。

要实现动态匹配证书的需求,可以用以下两种方法:

方法1:使用ssl_certificate_by_lua(OpenResty推荐)

如果使用OpenResty(带Lua模块的Nginx),可以通过Lua脚本在SSL握手阶段动态获取域名并加载对应证书:

  1. 确保OpenResty已安装,在http块中添加Lua配置:
http {
    lua_shared_dict cert_cache 10m;

    ssl_certificate_by_lua_block {
        local domain = ngx.ssl.server_name()
        local cert_path = "/etc/letsencrypt/live/" .. domain .. "/fullchain.pem"
        local key_path = "/etc/letsencrypt/live/" .. domain .. "/privkey.pem"

        local cert, err = io.open(cert_path, "r")
        if not cert then
            ngx.log(ngx.ERR, "Failed to open cert file: ", err)
            return ngx.exit(ngx.ERROR)
        end
        local cert_data = cert:read("*a")
        cert:close()

        local key, err = io.open(key_path, "r")
        if not key then
            ngx.log(ngx.ERR, "Failed to open key file: ", err)
            return ngx.exit(ngx.ERROR)
        end
        local key_data = key:read("*a")
        key:close()

        local ok, err = ngx.ssl.set_cert(cert_data)
        if not ok then
            ngx.log(ngx.ERR, "Failed to set cert: ", err)
            return ngx.exit(ngx.ERROR)
        end

        local ok, err = ngx.ssl.set_priv_key(key_data)
        if not ok then
            ngx.log(ngx.ERR, "Failed to set private key: ", err)
            return ngx.exit(ngx.ERROR)
        end
    }
}
  1. 修改server块,移除原来的ssl_certificate和ssl_certificate_key配置:
server {
    listen 443 ssl;
    server_name _; # 使用下划线匹配所有域名

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 20M;
...
}

这种方法能实时匹配新添加的域名,无需重启Nginx。

方法2:使用自动生成配置脚本(纯Nginx)

如果不想用OpenResty,可以编写脚本定期扫描证书目录,自动生成对应Nginx配置:

  1. 编写bash脚本,遍历证书目录生成配置片段:
#!/bin/bash
NGINX_CONF_DIR="/etc/nginx/conf.d"
CERT_BASE="/etc/letsencrypt/live"

# 清空旧配置
rm -f ${NGINX_CONF_DIR}/dynamic_ssl.conf

# 遍历所有域名文件夹
for domain in $(ls ${CERT_BASE}); do
    if [ -f "${CERT_BASE}/${domain}/fullchain.pem" ] && [ -f "${CERT_BASE}/${domain}/privkey.pem" ]; then
        cat >> ${NGINX_CONF_DIR}/dynamic_ssl.conf <<EOF
server {
    listen 443 ssl;
    server_name ${domain};

    ssl_certificate ${CERT_BASE}/${domain}/fullchain.pem;
    ssl_certificate_key ${CERT_BASE}/${domain}/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    client_max_body_size 20M;
    # 其他通用配置
...
}
EOF
    fi
done

# 重载Nginx
nginx -s reload
  1. 将脚本加入crontab,定期执行,即可自动适配新添加的域名。

注意事项

  • 方法1需要OpenResty支持,适合需要动态实时生效的场景
  • 方法2是纯Nginx方案,但需要定期重载配置,适合域名添加频率不高的场景
  • 确保/etc/letsencrypt/live/目录下的域名文件夹名称与实际请求的域名完全一致(比如请求test.example.com,文件夹名称必须是test.example.com),否则证书路径会出错

内容的提问来源于stack exchange,提问作者Najmus Sakib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:01:29