Nginx Proxy Manager 证书管理完全指南:HTTP 验证、DNS 验证与自定义证书的签发、续期与排障

Nginx Proxy Manager 证书管理完全指南:HTTP 验证、DNS 验证与自定义证书的签发、续期与排障 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-managerNginx Proxy ManagerNPM以“简单而强大的界面”管理 Nginx 反代主机而闻名而其证书Certificates模块是 HTTPS 落地的核心入口它统一了 Lets Encrypt HTTP 验证、DNS 验证和自定义证书上传三种签发路径并内置自动续期定时器。本文以项目仓库中 Certificates.md日语帮助文档为骨架结合后端 certificate.js 的实现细节、dns-plugins.json 的插件清单与前端弹窗组件源码完整讲解三种证书类型的适用场景、创建步骤、底层工作原理与运维注意事项读完即可在 NPM 中正确选择并落地 HTTPS 证书方案。三种证书类型总览仓库的日语帮助文档将证书分为三类对应前端 Certificates 页面顶部的三个创建入口详见前端弹窗组件 HTTPCertificateModal.tsx、DNSCertificateModal.tsx 与 CustomCertificateModal.tsx类型验证方式是否需要先建代理主机通配符Wildcard支持证书提供方适用场景HTTP 证书Lets Encrypt via HTTPLets Encrypt 服务器通过 HTTP80 端口访问你的域名验证所有权需要且必须可经 HTTP 访问并指向本 NPM不支持Lets Encrypt域名已解析到本机、80 端口可公网访问的常规站点DNS 证书Lets Encrypt via DNS通过 DNS 提供商插件在域名上创建临时 TXT 记录Lets Encrypt 查询该记录验证所有权不需要支持Lets Encrypt内网/无法开放 80 端口、或需要通配符证书的场景自定义证书Custom Certificate上传由你自己的证书颁发机构CA签发的证书不需要取决于你的证书任意 CA使用企业 CA、商业证书或自签证书的场景文档特别强调两个关键区别HTTP 验证方式不支持通配符而 DNS 验证方式支持通配符DNS 验证不需要预先创建代理主机也不需要代理主机开放 HTTP 访问。HTTP 验证证书最简签发路径前置条件与原理HTTP 验证证书的签发逻辑用帮助文档的原话概括是Lets Encrypt 服务器会尝试通过 HTTP注意不是 HTTPS访问你的域名若能成功访问就会签发证书。因此创建这类证书前你必须满足已经为域名创建了代理主机Proxy Host且该代理主机配置为可经 HTTP 访问该域名必须解析并指向当前 Nginx Proxy Manager 实例且 80 端口能从公网访问。从后端源码看签发流程远比“填个表单”复杂。在 certificate.js 的create方法第 117-228 行中HTTP 验证的完整调用链是通过internalHost.getHostsWithDomains()找出正在使用这些域名的所有主机disableInUseHosts()在签发期间临时禁用这些主机的 Nginx 配置避免与 ACME challenge 冲突internalNginx.generateLetsEncryptRequestConfig()生成临时的 Lets Encrypt 请求配置并reloadNginx调用requestLetsEncryptSsl()执行 certbot 命令签发证书签发成功后删除临时配置、再次 reload、enableInUseHosts()恢复此前禁用的主机配置。requestLetsEncryptSsl()第 777-817 行实际拼装的 certbot 命令如下certbot certonly -n \ --config /etc/letsencrypt.ini \ --work-dir /tmp/letsencrypt-lib \ --logs-dir /data/logs \ --cert-name npm-证书ID \ --agree-tos \ --authenticator webroot \ -m 你的邮箱 \ --preferred-challenges http \ --domains 域名1,域名2其中--cert-name npm-id决定了证书在容器内的落盘路径/etc/letsencrypt/live/npm-id/见getLiveCertPath()第 1262-1264 行。Nginx 模板 _certificates.conf 中ssl_certificate与ssl_certificate_key即指向该目录下的fullchain.pem和privkey.pem。证书签发后的使用与续期约束帮助文档指出证书签发后你可以编辑代理主机让它在 HTTPS 连接中使用这张证书但为了让证书能够续期该代理主机仍需保持配置为 HTTP 访问。原因正如上文调用链所示——每次续期renew都会重新走 HTTP challenge如果代理主机已强制跳转 HTTPS 或关闭了 80 端口Lets Encrypt 将无法访问 challenge 路径。关于续期仓库默认机制是自动续期定时器initTimer()第 38-46 行在应用启动时初始化一个每小时触发一次的定时器intervalTimeout: 1000 * 60 * 60每次触发调用processExpiringHosts()第 51-110 行查询所有provider letsencrypt且expires_on距今不足30 天renewBeforeExpirationBy: [30, days]的证书逐个调用renew()串行续期。代码注释特别说明续期必须串行执行否则会报 “Another instance of Certbot is already running.” 错误。你也可以在证书列表中手动点击“续期”按钮触发即时续期对应 APIPOST /api/nginx/certificates/:id/renew见 certificates.js。签发前的连通性自检HTTP 弹窗组件 HTTPCertificateModal.tsx 提供了Test按钮点击后会调用POST /api/nginx/certificates/test-http接口。后端testHttpsChallenge()certificate.js会先在/data/letsencrypt-acme-challenge/.well-known/acme-challenge/下写入测试文件再通过第三方 REST 测试服务对每个域名发起 HTTP 请求验证 challenge 路径可达性最后返回每个域名的探测结果返回值含义对应前端文案ok服务器存在且返回了正确数据可正常签发no-host域名未解析/主机不可达reachability-not-resolved404服务器存在但返回 404wrong-data服务器存在但返回了错误数据failed无法完成探测other:code其他错误码强烈建议在点“保存”之前先点“测试”可以提前暴露 DNS 解析、80 端口、Nginx 配置等问题避免签发失败后还要排查。DNS 验证证书支持通配符的无端口方案原理与前置条件帮助文档对 DNS 验证的定义是需要 DNS 提供商插件DNS Provider plugin。插件会在你的域名上创建临时记录Lets Encrypt 查询该记录确认你拥有该域名后签发证书。相较 HTTP 验证DNS 验证有两大优势不需要预先创建代理主机也不要求代理主机开放 HTTP 访问——即使站点完全在内网、或 80/443 端口未对外开放也能签发证书支持通配符域名Wildcard例如*.example.com一次签发即可覆盖所有子域名。后端在create方法中专门为 DNS challenge 走独立分支第 153-166 行由于 DNS 验证不需要在 Nginx 中生成临时配置代码注释 “With DNS challenge no config is needed, so skip 3 and 5.”流程简化为reload→requestLetsEncryptSslWithDnsChallenge()签发 →reload→ 恢复之前禁用的主机。DNS 提供商的动态安装机制DNS 验证的核心是插件系统。后端启动时通过GET /api/nginx/certificates/dns-providers接口见 certificates.js返回 dns-plugins.json 中声明的全部提供商清单前端由DNSProviderFields.tsx的useDnsProvidershook 拉取并渲染为下拉框。插件是按需安装的签发证书时requestLetsEncryptSslWithDnsChallenge()第 824-889 行首先调用installPlugin()certbot.js——该函数读取dns-plugins.json中对应插件的package_name与version版本串中的{{certbot-version}}会被替换为环境变量CERTBOT_VERSION然后执行. /opt/certbot/bin/activate pip install --no-cache-dir 依赖 package_nameversion deactivate即首次使用时通过 pip 在容器的 certbot 虚拟环境中安装对应插件。这也解释了为什么第一次用某个 DNS 提供商签发证书会比后续慢——需要先下载安装插件。以 Cloudflare 为例其插件条目为cloudflare: { credentials: dns_cloudflare_api_token0123456789abcdef0123456789abcdef01234567, dependencies: acme{{certbot-version}}, full_plugin_name: dns-cloudflare, name: Cloudflare, package_name: certbot-dns-cloudflare, version: {{certbot-version}} }创建 DNS 证书的表单字段对应前端弹窗 DNSCertificateModal.tsx 与表单组件 DNSProviderFields.tsx需要填写域名Domain Names可填通配符域名isWildcardPermitted且dnsProviderWildcardSupported均开启Key Typersa或ecdsa弹窗默认ecdsaDNS Provider下拉选择提供商列表来自dns-plugins.json按名称排序Credentials凭据代码编辑器形式的多行文本内容由所选插件在credentials字段中给出的模板自动预填选中提供商后handleChange会把newValue.credentials写入meta.dnsProviderCredentialsPropagation Seconds传播等待秒数0~7200之间的整数即等待 DNS 记录全球生效的时间。这些字段最终写入证书对象的meta数据结构见 certificate-object.json 的meta定义dns_challenge、dns_provider、dns_provider_credentials、propagation_seconds、key_type。底层requestLetsEncryptSslWithDnsChallenge()生成的 certbot 命令形如certbot certonly -n \ --config /etc/letsencrypt.ini \ --work-dir /tmp/letsencrypt-lib \ --logs-dir /data/logs \ --cert-name npm-证书ID \ --agree-tos -m 你的邮箱 \ --preferred-challenges dns \ --domains *.example.com,example.com \ --authenticator full_plugin_name \ --full_plugin_name-credentials /etc/letsencrypt/credentials/credentials-证书ID \ [--full_plugin_name-propagation-seconds 秒数] \ [--key-type ecdsa|rsa]值得注意的实现细节凭据文件以0600 权限写入/etc/letsencrypt/credentials/credentials-证书ID第 831-833 行签发完成后即删除route53是特例——它不传--credentials参数而是通过环境变量AWS_CONFIG_FILE指向凭据文件见getAdditionalCertbotArgs()第 1238-1260 行duckdns提供商额外追加--dns-duckdns-no-txt-restore参数若配置了propagation_seconds会追加--plugin-propagation-seconds参数。目前 dns-plugins.json 收录了 100 家提供商覆盖主流云厂商与域名服务商包括 Cloudflare、Aliyun、Azure、Google、DigitalOcean、GoDaddy、Hetzner、Linode、Namecheap、OVH、PowerDNS、Route 53Amazon、Tencent Cloud、DNSPod、DuckDNS、Vultr 等。DNS 证书的续期DNS 证书同样受每小时定时器自动续期管理。renew()方法第 897-927 行会根据certificate.meta.dns_challenge分派到renewLetsEncryptSslWithDnsChallenge()第 974-1014 行其 certbot 命令使用renew --force-renewal --preferred-challenges dns并携带相同的凭据文件参数。续期成功后后端会从fullchain.pem解析新的到期时间并更新数据库记录。自定义证书上传自有 CA 证书适用场景与约束帮助文档对自定义证书的描述很简短使用此选项上传由你自己的证书颁发机构提供的证书。这适用于企业/学校内部 CA 签发的证书商业证书如收费 OV/EV 证书自签名证书测试环境。创建时只需填写一个昵称Nice Name之后在证书详情页通过“上传证书文件”入口提交证书材料。支持的文件与校验逻辑后端allowedSslFiles第 32 行限定了三种可上传文件certificate证书、certificate_key私钥、intermediate_certificate中间证书。upload()第 597-624 行与validate()第 554-588 行会用 openssl 对每个文件做实际校验私钥通过openssl pkey -in file -check -noout校验输出须包含 “key is valid”若 10 秒内未返回例如私钥带 passphrase 加密导致卡住会抛出 “Validation timed out” 错误——因此上传的私钥不能带密码保护证书/中间证书通过openssl x509 -subject/-issuer/-dates -noout解析 CN、签发者与有效期若证书已过期会拒绝getCertificateInfo的throwExpired参数。校验通过后writeCustomCert()第 487-530 行会把证书写入/data/custom_ssl/npm-证书ID/目录fullchain.pem若提供了中间证书会拼接在其后与privkey.pem。这与 _certificates.conf 中自定义证书分支的路径一一对应# Custom SSL ssl_certificate /data/custom_ssl/npm-{{ certificate_id }}/fullchain.pem; ssl_certificate_key /data/custom_ssl/npm-{{ certificate_id }}/privkey.pem;也就是说NPM 把证书内容与 Nginx 配置生成分离无论证书来自哪种渠道最终模板都只引用固定的fullchain.pem/privkey.pem路径由 provider 类型决定指向 Lets Encrypt 目录还是自定义目录。数据模型与 API 概览从数据模型 certificate.js 看证书表certificate的关键字段包括providerletsencrypt/other、nice_name自定义证书的显示名、domain_namesJSON 数组插入时自动排序去重、expires_on、metaJSON存储 DNS 插件信息或证书内容。provider letsencrypt时nice_name自动取domain_names.join(, )见 certificate.js。模型还定义了证书与proxy_hosts、dead_hosts、redirection_hosts、streams的四组一对多关联这意味着同一张证书可以被多种类型的主机共用例如同时被代理主机与 404 主机引用删除证书前系统会检查其是否仍被引用。完整 API 端点集中在 certificates.js方法路径用途GET/api/nginx/certificates证书列表支持expand与搜索POST/api/nginx/certificates创建证书Lets Encrypt 签发最长 15 分钟超时GET/api/nginx/certificates/dns-providers获取支持的 DNS 提供商清单POST/api/nginx/certificates/test-http测试 HTTP challenge 可达性POST/api/nginx/certificates/validate上传前校验证书文件GET/api/nginx/certificates/:id证书详情可expand关联主机DELETE/api/nginx/certificates/:id删除证书POST/api/nginx/certificates/:id/upload上传自定义证书文件POST/api/nginx/certificates/:id/renew手动续期 Lets Encrypt 证书GET/api/nginx/certificates/:id/download下载 Lets Encrypt 证书打包为 zip选型建议与常见问题怎么选HTTP 还是 DNS域名已解析到本机、80 端口公网可达、且只需要单域名→ HTTP 验证最省事无需任何 API 凭据站点在内网/防火墙后、80 端口不可达→ 只能选 DNS 验证需要通配符证书*.example.com→ 必须选 DNS 验证HTTP 方式不支持域名托管在列表内的 DNS 提供商→ 优先 DNS 验证凭据仅需创建一次 API Token企业合规/内部系统→ 自定义证书上传。常见问题排查HTTP 签发失败先用弹窗里的 Test 按钮自检确认代理主机已存在、域名 A 记录指向本机、80 端口未被防火墙/运营商封锁、代理主机没有配置强制 HTTPS 跳转。DNS 签发失败检查 DNS 提供商凭据模板是否完整填写如dns_cloudflare_api_token确认该 API Token 对目标域名有 DNS 记录的增删权限必要时调大 Propagation Seconds。续期失败HTTP 证书要确保代理主机始终保留 HTTP 访问文档明确要求检查定时器日志Renewing SSL certs expiring within 30 days ...与Completed SSL cert renew process见 certificate.js。自定义证书上传被拒私钥不得带 passphrase证书不能已过期若提示超时多半是私钥加密导致 openssl 校验卡住。删除 Lets Encrypt 证书后端会先吊销证书revokeLetsEncryptSsl()再逻辑删除记录is_deleted 1见 certificate.js。总结Nginx Proxy Manager 的证书模块把 Lets Encrypt 的两种 ACME 验证方式与自定义证书上传统一到一个可视化管理界面中并在其后端用 certbot 动态 pip 插件 每小时自动续期定时器实现了“签发—落盘—引用—续期”的全生命周期闭环。理解 HTTP 验证需代理主机、不支持通配符与 DNS 验证免主机、支持通配符、需插件凭据的本质区别是正确选型的关键而自定义证书则提供了完全脱离 Lets Encrypt 的灵活性。本文所述的所有路径、命令与文件均可对照仓库源码backend/internal/certificate.js、backend/certbot/dns-plugins.json、backend/templates/_certificates.conf、backend/routes/nginx/certificates.js逐行验证。【免费下载链接】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),仅供参考