Nginx Proxy Manager 证书全指南:HTTP / DNS 验证与自定义证书实战解析
【免费下载链接】nginx-proxy-managerDocker container for managing Nginx proxy hosts with a simple, powerful interface项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager
本篇技术指南以 Nginx Proxy Manager 的官方帮助文档(前端帮助文档)为主线,系统讲解证书管理模块的三大核心方案:Let's Encrypt HTTP 验证证书、Let's Encrypt DNS 验证证书(含 Wildcard 通配符支持)以及自定义证书上传。文章不仅覆盖每一步操作流程,还将结合仓库中 证书后端实现、API 路由、DNS 插件清单 与前端表单组件,深入剖析每种方案背后的底层工作原理,帮助你理解"证书为什么这样申请、何时该选哪种方式、失败时如何排查",从而在真实生产环境中做出正确选择。
三种证书方案一览
Nginx Proxy Manager(NPM)的"证书(Certificates)"页面提供了三种创建证书的方式,它们分别对应不同的验证链路与适用场景:
| 方案 | 验证方式 | 是否支持通配符 | 是否需要先建 Proxy Host | 代表场景 |
|---|---|---|---|---|
| HTTP 证书(Let's Encrypt via HTTP) | HTTP 文件验证 | 否 | 是 | 已有公网可达的反向代理域名 |
| DNS 证书(Let's Encrypt via DNS) | DNS TXT 记录验证 | 是 | 否 | 通配符证书、内网/未开放 80 端口、CDN 后置站点 |
| 自定义证书(Custom) | 由你的 CA 签发 | 由证书本身决定 | 否 | 企业级证书、付费证书、内部 PKI |
三者最终都以provider字段区分:letsencrypt与other。在 证书数据模型 中,证书记录包含provider、nice_name、domain_names、expires_on和meta等核心字段,其中meta用于保存 DNS 挑战信息、私钥内容、密钥类型等扩展属性,其完整结构定义在 certificate-object.json。
HTTP 验证证书(HTTP Certificate)
工作原理:Let's Encrypt 如何验证域名归属
HTTP 验证(HTTP-01 Challenge)的流程是:Let's Encrypt 的服务器会**通过 HTTP(而非 HTTPS)**访问你的域名,若访问成功并取到预期的令牌内容,即认定你拥有该域名,随后签发证书。
在 Nginx Proxy Manager 中,这一过程由 requestLetsEncryptSsl 方法驱动,其核心是通过certbot certonly命令,配合--authenticator webroot与--preferred-challenges http参数完成验证。整个申请过程还会经历以下 6 个步骤(见 create 方法 中的注释):
- 找出所有使用了本证书域名的 Host(
getHostsWithDomains); - 将这些 Host 在 Nginx 中临时禁用(
disableInUseHosts); - 生成临时的 Let's Encrypt 请求配置(
generateLetsEncryptRequestConfig); - 调用 certbot 申请证书;
- 删除临时的 LE 配置(
deleteLetsEncryptRequestConfig); - 重新启用之前禁用的 Host(
enableInUseHosts)。
这一"临时禁用"策略是有意为之:证书申请期间,Nginx 必须只响应 ACME 挑战请求,因此需要临时生成一个仅监听 80 端口的虚拟主机。该配置由 letsencrypt-request.conf 模板生成,其中server_name直接绑定本次申请的全部域名,并通过include conf.d/include/letsencrypt-acme-challenge.conf暴露 ACME 挑战目录;其余所有请求统一return 404,避免影响挑战验证结果。
前置条件:必须先创建指向本机的 Proxy Host
这是 HTTP 验证方式最容易踩坑的地方。由于 Let's Encrypt 是从公网发起 HTTP 访问,因此你必须为待签发证书的域名先创建一个 Proxy Host,且满足:
- 该 Host 必须能通过 HTTP(80 端口)访问;
- 该 Host 必须指向当前这台 Nginx Proxy Manager 实例。
证书签发成功后,你可以修改该 Proxy Host,让它同时使用此证书提供 HTTPS 服务。但请务必记住:该 Proxy Host 必须始终保留 HTTP 访问配置,否则后续证书续期(Renew)时 ACME 挑战将无路可走,导致续期失败。
操作步骤
- 进入Certificates页面,点击Add SSL Certificate,选择Let's Encrypt Certificate;
- 在弹出的 HTTPCertificateModal 表单中填写域名列表(Domain Names);
- 选择密钥类型(Key Type):
rsa或ecdsa(默认ecdsa,详见下文说明); - 可选:点击Test按钮,对域名做 HTTP 可达性预检;
- 点击Save保存并触发签发请求。
值得注意的是,NPM 在保存前内置了一个 HTTP 挑战预检功能:testHttpsChallenge(backend/internal/certificate.js)会在.well-known/acme-challenge目录写入测试文件,再通过外部服务发起 HTTP 探测。前端会根据返回结果展示五类诊断信息(见 HTTPCertificateModal.tsx):
| 探测结果 | 含义 |
|---|---|
ok | 可达且返回内容正确,可以申请 |
no-host | 域名解析不到主机,检查 DNS |
404 | 主机可达但返回 404,检查站点配置 |
wrong-data | 返回内容不是预期令牌 |
failed/other:* | 探测本身失败或返回未知状态码 |
重要限制:不支持通配符
HTTP 验证过程不支持 Wildcard(通配符)域名。原因很直接:HTTP-01 挑战需要精确匹配具体主机名,*.example.com无法通过 HTTP 方式完成验证。如果需要*.example.com这类通配符证书,必须改用下面的 DNS 验证方式。
DNS 验证证书(DNS Certificate)
工作原理:通过 DNS 插件自动添加 TXT 记录
DNS 验证(DNS-01 Challenge)的核心思路是:你使用某个DNS Provider 插件,插件会在你的域名解析服务商处自动创建一条临时的 TXT 记录;Let's Encrypt 随后查询该记录,确认你对该域名具备控制权后签发证书。验证完成后,NPM 会清理掉这条临时记录。
整个流程由 requestLetsEncryptSslWithDnsChallenge 驱动。它的特别之处在于:
- 会先调用
installPlugin安装对应的 certbot DNS 插件(插件清单见 dns-plugins.json); - 将你填写的
dns_provider_credentials以0600权限写入/etc/letsencrypt/credentials/credentials-<certificate_id>,避免凭证泄露(0600仅允许属主读写); - 调用
certbot certonly并指定--preferred-challenges dns与对应插件的--authenticator; - 支持通过
meta.propagation_seconds自定义 DNS 传播等待时间(默认值视插件而定),等待时间不足时验证可能失败; - Route 53 插件比较特殊:它不通过
--credentials参数,而是通过环境变量AWS_CONFIG_FILE注入凭证(见 getAdditionalCertbotArgs)。
由于验证完全发生在 DNS 层面,申请 DNS 证书前不需要先创建任何 Proxy Host,也不要求 Host 开放 HTTP 访问——这对内网服务、尚未上线的新站点、以及源站位于 CDN 之后的场景非常友好。
前置条件:准备 DNS Provider 的 API 凭证
要使用 DNS 验证,你必须拥有 DNS 托管服务商的 API 凭证(Token / Secret / Access Key 等),并在 NPM 界面中正确填写。仓库 dns-plugins.json 内置了大量提供商的支持定义,例如:
# Cloudflare dns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567 # Aliyun dns_aliyun_access_key = 12345678 dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef # DNSPod dns_dnspod_email = "email@example.com" dns_dnspod_api_token = "id,key" # DigitalOcean dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff # Namecheap dns_namecheap_username = 123456 dns_namecheap_api_key = 0123456789abcdef0123456789abcdef01234567不同插件要求的字段完全不同(如 Google 需要 JSON 格式的服务账号、Azure 需要 SP/MSI 认证信息、RFC 2136 需要 TSIG 密钥),创建前请先确认你的 DNS 服务商是否在清单中,并按该插件要求的格式填写凭证。凭证填写后保存在meta.dns_provider_credentials中,API 返回给前端时会被过滤(见 internal/certificate.js 的omissions定义),避免敏感信息泄露。
操作步骤
- 进入Certificates页面,点击Add SSL Certificate,选择Let's Encrypt Certificate(DNS Challenge);
- 在 DNSCertificateModal 中填写域名。由于 DNS 验证支持通配符,这里允许输入
*.example.com; - 选择密钥类型(Key Type);
- 在 DNS Provider 字段中选择你的服务商,并按模板粘贴对应凭证(见 DNSProviderFields);
- 点击Save保存并触发签发。
重要特性:支持 Wildcard 通配符
与 HTTP 验证不同,DNS 验证过程支持通配符域名。这也是生产环境中申请*.example.com证书的标准路径。签发完成后,该证书既可用于example.com根域名,也可覆盖*.example.com的所有子域名。
自定义证书(Custom Certificate)
适用场景
当你已经持有由自有 CA(证书颁发机构)签发的 SSL 证书时(例如企业内部的 PKI 证书、付费商业证书、或由其他 ACME 客户端签发的证书),可以使用Custom方式直接上传,NPM 不参与签发流程。
上传的三个文件
在 CustomCertificateModal 中,需要提供:
| 字段 | 说明 | 是否必填 |
|---|---|---|
| 证书(Certificate) | 站点证书主体(PEM 格式) | 是 |
| 证书密钥(Certificate Key) | 与证书配对的私钥(PEM 格式) | 是 |
| 中间证书(Intermediate Certificate) | 证书链中的中间 CA 证书 | 否(建议填写以保证完整信任链) |
保存前的三步校验
前端在提交时并非直接保存,而是经历"先校验、再建记录、再上传"三步(对应 CustomCertificateModal.tsx 的onSubmit):
- 校验(validate):调用
POST /api/nginx/certificates/validate,后端 validate 方法 会分别对私钥执行openssl pkey -check校验(见 checkPrivateKey),对证书执行openssl x509解析(见 getCertificateInfo),并检查证书是否已过期(throwExpired参数)——过期证书会被直接拒绝; - 创建(create):以
provider: "other"创建一条证书记录; - 上传(upload):将证书文件内容写入数据库,并由 writeCustomCert 写入磁盘目录
/data/custom_ssl/npm-<certificate_id>/,生成fullchain.pem(自动拼接中间证书)与privkey.pem。
上传时后端会通过allowedSslFiles白名单(certificate、certificate_key、intermediate_certificate)过滤文件,防止无关内容混入(见 upload 方法)。
证书生命周期管理:续期、下载与删除
无论通过哪种方式创建,证书都具备完整的生命周期管理能力,对应后端 API 路由定义于 routes/nginx/certificates.js。
自动续期:到期前 30 天
Nginx Proxy Manager 内置了一个自动续期定时器(initTimer / processExpiringHosts):
- 每1 小时检查一次所有
provider = letsencrypt且未删除的证书; - 凡是
expires_on在30 天内到期的证书,都会触发自动续期(renewBeforeExpirationBy = [30, "days"]); - 续期任务必须串行执行(按证书逐个排队),因为 certbot 不允许两个实例并发运行(源码注释明确提示
Another instance of Certbot is already running错误)。
续期成功后,后端会从磁盘上的fullchain.pem重新解析到期时间并更新数据库(见 renew 方法)。需要说明的是:自动续期仅适用于 Let's Encrypt 证书,自定义证书需要你自行在 CA 侧处理续期。
手动续期与下载
- Renew:在证书列表中对 Let's Encrypt 证书点击续期,调用
POST /api/nginx/certificates/:id/renew。由于 ACME 挑战可能耗时较长,路由设置了15 分钟超时(req.setTimeout(900000)),创建证书的接口同样适用。 - Download:Let's Encrypt 证书支持打包下载(
GET /api/nginx/certificates/:id/download)。后端会将/etc/letsencrypt/live/npm-<id>/下的.pem文件打包为npm-<id>-<时间戳>.zip(见 download / zipFiles)。自定义证书不支持下载(接口会抛出Only Let'sEncrypt certificates can be downloaded)。
删除与吊销
删除证书时,如果是 Let's Encrypt 证书,后端会同步调用certbot revoke --delete-after-revoke执行吊销(见 revokeLetsEncryptSsl),并清理对应的 DNS 凭证文件;删除操作本身是软删除(is_deleted = 1),见 delete 方法。
密钥类型(Key Type)与元数据
创建 Let's Encrypt 证书时,前端表单会提供Key Type选项(rsa/ecdsa,默认ecdsa)。该值保存在meta.key_type中,后端组装 certbot 命令时会追加--key-type <type>参数(见 requestLetsEncryptSsl 与 requestLetsEncryptSslWithDnsChallenge)。key_type的合法取值在 certificate-object.json 中枚举为["rsa", "ecdsa"]。生产环境建议:
- 需要与旧客户端(如老 Android 设备)兼容时选择
rsa; - 追求更小的握手开销、现代客户端环境选择
ecdsa。
meta对象是证书扩展信息的核心容器,支持的字段包括(见 certificate-object.json):
| 字段 | 类型 | 说明 |
|---|---|---|
certificate/certificate_key/intermediate_certificate | string | 自定义证书的 PEM 内容 |
dns_challenge | boolean | 是否为 DNS 验证方式 |
dns_provider | string | DNS 插件标识(对应 dns-plugins.json 的 key) |
dns_provider_credentials | string | DNS 插件凭证(API 返回时被过滤) |
propagation_seconds | integer | DNS 记录传播等待秒数 |
key_type | enum | rsa/ecdsa |
letsencrypt_certificate | object | 签发后回填的证书信息(CN、issuer、有效期) |
常见问题排查(FAQ)
HTTP 验证失败怎么办?
按 HTTPCertificateModal 的预检结果逐项排查:
- no-host:域名 A 记录未解析到本机公网 IP,检查 DNS;
- 404:80 端口可访问但路径不对,确认 Proxy Host 指向本机且未覆盖 ACME 挑战目录;
- wrong-data:返回内容非预期令牌,可能存在其他服务抢占了
/路径或反向代理规则未生效; - failed / other:临时网络故障,稍后重试或检查防火墙。
另外确认:申请域名对应的 Proxy Host 必须保留 HTTP(80)访问;如果使用了 CDN 或强制跳转 HTTPS 的规则,ACME 挑战可能被拦截。
DNS 验证失败怎么办?
- 确认 DNS 凭证格式与 dns-plugins.json 中该插件的
credentials模板完全一致; - 确认 API 账号具备新增/删除 TXT 记录的权限;
- 若你的 DNS 服务商解析生效较慢,可适当调大
propagation_seconds(等待传播后再让 Let's Encrypt 查询); - 检查插件是否已安装:NPM 会在请求时自动安装(
installPlugin),安装失败会直接导致签发失败。
证书续期失败?
- 对于 HTTP 验证证书:检查对应 Proxy Host 是否仍开放 80 端口 HTTP 访问;
- 对于 DNS 验证证书:检查 DNS 凭证是否仍有效(凭证过期/轮换后需重新保存证书配置);
- 查看容器内日志目录
/data/logs/(certbot 日志位于--logs-dir /data/logs,另见 letsencrypt-request.conf 中的access_log/error_log配置)确认 certbot 具体报错。
总结
Nginx Proxy Manager 的证书模块通过"HTTP 验证、DNS 验证、自定义证书"三种路径,覆盖了从入门到生产环境的全部证书需求:HTTP 验证最简单,但要求先有公网可达的 Proxy Host 且不支持通配符;DNS 验证多一步插件凭证配置,但换来通配符支持与更强的部署灵活性;自定义证书则完全托管外部 CA 签发的证书。理解这三种方案背后的 ACME 挑战原理(结合 internal/certificate.js 中 certbot 命令的组装逻辑),能让你在配置与排障时游刃有余。
【免费下载链接】nginx-proxy-managerDocker container for managing Nginx proxy hosts with a simple, powerful interface项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考