十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Nginx 中 PEM 与 CRT 证书格式详解及 SSL 配置实战

Nginx 中 PEM 与 CRT 证书格式详解及 SSL 配置实战 先别急着去搜什么“crt 下载”我见过太多人把证书文件当成稀有资源到处找结果打开一看里面就是一段以-----BEGIN CERTIFICATE-----开头的 Base64 文本。做 Nginx 配置这十年几乎每次遇到 HTTPS 报错最后都能归到几个老问题上.pem和.crt到底有什么区别、证书链为什么不完整、私钥是不是和证书对不上。这篇文章我就把 Nginx 里和.pem、.crt相关的底细一次讲透从格式原理到生成证书、配置站点再到运维排查尽量用我踩过的坑帮你把弯路省掉。适合刚接触 Nginx SSL 配置的运维新手也适合每次配证书都要现查资料的老手。1. 先搞清楚.pem和.crt到底是不是一种东西1.1 PEM 格式的来历Privacy Enhanced Mail 和现代使用PEM 全称是 Privacy Enhanced Mail这名字一听就知道和电子邮件有关。它最初是为邮件加密设计的一套文本协议最早出现在 1993 年左右的 RFC 标准里。后来邮件加密这套体系没有全面普及但这种用 Base64 编码的文本格式却被 OpenSSL 等加密库沿用下来成为保存密钥、证书、CSR 等对象的事实标准。一个典型的 PEM 文件长这样-----BEGIN CERTIFICATE----- MIIBmzCCA...中间是一大串 Base64 字符 -----END CERTIFICATE-----所以现在的 PEM 文件本质上和“邮件加密”已经没有关系它只是一种“外包装”对二进制 DER 数据进行 Base64 编码再在外面加上可读的起始头和结束头。Nginx 的ssl_certificate指令只认这种 PEM 文本格式所以你在网上看到很多教程说“Nginx 证书要 PEM 格式”说的就是文件里必须包含那两行BEGIN/END标记。1.2.crt和.pem的区别与混用场景严格来说.crt只是 Certificate 的缩写表示文件内容是一个证书并没有规定编码格式。在 Windows 系统里.crt常见有两种形态一种是 DER 二进制双击可以导入系统证书库另一种是 PEM 文本和.pem没区别。在 Linux 和 Nginx 环境下绝大多数.crt文件其实就是 PEM 文本。所以结论是在 Nginx 配置里.pem和.crt可以互换改一下后缀名完全不影响使用。判断标准不是后缀而是内容里有没有那两行BEGIN/END头。如果你拿到一个.crt文件用cat或vim打开能看到-----BEGIN CERTIFICATE-----那它就是一个 PEM 文件如果打开是一堆二进制乱码那么它是 DER 编码需要先转换。后缀常见编码使用场景Nginx 是否兼容.pemPEM 文本Linux、OpenSSL、Nginx是.crtPEM 文本或 DER 二进制Windows / Linux 通用仅 PEM 文本兼容.derDER 二进制Windows 导入证书库否需转换.cer / .crt常见于 Windows双击导入证书库否需转换为 PEM1.3 证书链的组成叶证书、中间证书、根证书配置 Nginx 时很多人不理解为什么一个证书文件里有时候要多段BEGIN CERTIFICATE。这是因为一张完整的证书链通常有三层叶证书服务器证书就是给example.com签发的证书包含域名、公钥、有效期。中间证书由根证书签发再给叶证书签名用来桥接浏览器内置根证书和服务器证书。根证书由 CA 机构自己持有浏览器和操作系统已经内置不需要服务器发送。浏览器内置了根证书但一般不会内置中间证书。如果 Nginx 只把叶证书发给客户端而客户端本地找不到对应中间证书就会报“证书链不完整”或“unable to get local issuer certificate”。所以生产环境建议把叶证书和中间证书合并到一个.pem文件里再配置给 Nginx。很多 CA 下发的fullchain.pem已经帮我们合并好了但还有不少渠道会区分成server.crt和ca-bundle.crt这时候就需要手工合并。2. 准备证书的完整实操从生成私钥到合并证书链2.1 自签名证书生成命令含 SAN 扩展如果在内网测试环境或者暂时没有域名可以用自签名证书快速跑通 Nginx 的 HTTPS。下面这条命令是我的标准写法openssl req -x509 -newkey rsa:2048 -sha256 -days 365 \ -nodes \ -keyout example.key \ -out example.crt \ -subj /CNexample.com \ -addext subjectAltNameDNS:example.com,DNS:www.example.com解释一下每个参数req -x509直接生成自签名证书而不是生成 CSR。newkey rsa:2048同时生成新的 RSA 私钥位数 2048目前够了。sha256签名哈希算法。nodes私钥不加密。如果加密Nginx 每次启动时都要输入密码非常麻烦建议生成时直接去掉。keyout、out分别指定私钥和证书输出路径。subj证书主体信息这里设置 CN 为域名。addext添加 SAN 扩展这一步非常重要。现在浏览器对没有 SAN 的证书会报“NET::ERR_CERT_COMMON_NAME_INVALID”。如果你还要用 IP 访问可以在 SAN 里加IP:127.0.0.1之类的值。生成后example.crt就是 PEM 格式可以直接给 Nginx 用。2.2 从 CA 申请证书后的文件处理如果给正式环境用自签名证书不能让外部用户信任必须去 CA 申请。流程通常是先在本机生成一个 CSRopenssl req -new -newkey rsa:2048 -nodes \ -keyout example.key \ -out example.csr \ -subj /CNexample.com \ -addext subjectAltNameDNS:example.com,DNS:www.example.com生成后你会得到example.key和example.csr。把example.csr的内容复制给 CA他们审核通过后会返回证书文件。不同 CA 返回文件的方式不太一样有些给一个fullchain.pem直接包含叶证书和中间证书拿到就能用。有些给一个server.crt加一个ca-bundle.crt需要自己合并。有些只给一个cert.crt还需要去官网下载中间证书。另外要注意一点CA 网站下载文件时如果用 Windows 记事本打开并保存过可能会加一个 UTF-8 BOM 头导致证书的BEGIN标记前面多出不可见字符。Nginx 解析时会报错。拿到文件后先用cat或者head看一眼确保第一行就是-----BEGIN CERTIFICATE-----。2.3 证书链合并和格式转换命令假设你手里的文件是server.crt和ca-bundle.crt合并命令非常简单cat server.crt ca-bundle.crt fullchain.pem顺序必须注意服务器证书在前中间证书在后。cat拼接顺序反了Nginx 不一定报错但部分客户端可能不认。合并后可以用openssl crl2pkcs7或直接查看文本确认grep BEGIN CERTIFICATE fullchain.pem应该看到至少两段。如果只有一个说明合并没生效。如果拿到的证书是 DER 二进制用file命令能看出来比如ASCII text就是 PEMdata就是 DER。转换命令openssl x509 -inform DER -in cert.der -outform PEM -out cert.pem有时候还会遇到.pfx或.p12PKCS#12格式的证书包常见于 Windows 或者某些客户提供的文件。用下面的命令拆出来# 导出证书链不含私钥 openssl pkcs12 -in cert.pfx -nokeys -out cert.pem # 导出私钥 openssl pkcs12 -in cert.pfx -nodes -nocerts -out private.key执行时会要求输入 PFX 的密码如果正确得到的cert.pem和private.key都是 PEM 格式可以直接给 Nginx 用。2.4 检查证书与私钥是否匹配配置前这一步一定不要省。我遇到过不止一次线上事故最后发现是密钥和证书对不上。最简单的检查方法是比对两者生成的模数modulusopenssl x509 -in fullchain.pem -noout -modulus | openssl md5 openssl rsa -in example.key -noout -modulus | openssl md5两条命令输出的 MD5 值一致说明证书和私钥是配对的一对不一致就赶紧换文件。另外还可以验证证书链是否完整openssl verify -CAfile ca-bundle.crt fullchain.pem如果看到OK说明证书链没问题。如果报unable to get local issuer certificate说明中间证书缺失或顺序不对。3. Nginx 配置 SSL 的核心环节3.1 一个标准 HTTPS server 块模板直接给一个我常用且已经线上跑了好几年的模板server { listen 443 ssl; http2 on; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/example.fullchain.pem; ssl_certificate_key /etc/nginx/ssl/example.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; root /var/www/html; index index.html; }几个细节说明一下ssl_certificate可以指向.pem也可以指向.crt只要内容是 PEM 文本就行。ssl_certificate_key指向私钥文件权限建议设成 600至少保证 Nginx 运行用户能读。ssl_protocols里我没写 TLSv1.0 和 1.1这两个老协议都已不安全现在政企类项目也基本要求禁用。http2 on;用了 SSL 之后最好开启能明显提升并发性能前提是 Nginx 版本支持。配置路径建议放在/etc/nginx/ssl/下按域名分文件比如example.com.fullchain.pem后面换证书好找。写完配置后先检查再重载nginx -t nginx -s reload如果是 systemd 管理的系统也可以用systemctl reload nginx。重载不会中断服务所以线上更新证书时用这个命令即可。3.2 多域名/多项目的证书匹配方案热词里频繁出现“Nginx 部署多个 web 项目”这正好和证书配置强相关。如果你有两个独立域名a.example.com和b.example.com就想办法让 Nginx 根据域名自动选择对应证书也就是通过 SNIServer Name Indication机制来实现。常见配置是每个域名一个独立 server 块server { listen 443 ssl; server_name a.example.com; ssl_certificate /etc/nginx/ssl/a.fullchain.pem; ssl_certificate_key /etc/nginx/ssl/a.key; # 其余 a 站配置 } server { listen 443 ssl; server_name b.example.com; ssl_certificate /etc/nginx/ssl/b.fullchain.pem; ssl_certificate_key /etc/nginx/ssl/b.key; # 其余 b 站配置 }这种情况下不需要额外配置任何东西Nginx 在 TLS 握手阶段会读取客户端发送的 SNI 字段自动选择匹配的证书。这也意味着同一个 IP 可以承载多个不同域名的 HTTPS 站点不需要给每个域名单独开 IP。如果多个项目共用同一个域名只是在路径上做区分那就只用一个 server 块证书共用根据 location 转发到不同后端server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/example.fullchain.pem; ssl_certificate_key /etc/nginx/ssl/example.key; location /app1/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /app2/ { proxy_pass http://127.0.0.1:8082; # ... } }这里每个 location 是同一个证书但如果内部系统有不同的内部域名也可以再单独开一个 server 块做内网证书逻辑是一样的。3.3 反向代理和负载均衡里的证书透传和终止Nginx 做反向代理和负载均衡时最常见的是“HTTPS 终止”客户端到 Nginx 是 HTTPSNginx 解密后再用 HTTP 转发给后端。这种方式下后端服务只需要监听 HTTP 端口证书只需要配置在 Nginx 节点上。但有些场景后端也要求 HTTPS比如后端是另一个 Nginx 或者经过严格安全审计的服务。这时候要让 Nginx 在转发时也发起 TLS 连接location /api/ { proxy_pass https://backend_upstream; proxy_ssl_server_name on; proxy_set_header Host $host; } upstream backend_upstream { server 10.0.0.11:443 weight3; server 10.0.0.12:443 weight1; }proxy_ssl_server_name on;会在转发时携带 SNI 字段否则后端多证书环境中可能会选错默认证书。如果是双向 TLSmTLS还需要加proxy_ssl_certificate和proxy_ssl_certificate_key告诉后端“我是谁”。还有一个小知识点值得提一下热词里有“Nginx 转发会带 IP 头部的五元组信息吗”。其实 Nginx 反向代理默认只通过X-Real-IP和X-Forwarded-For传递客户端 IP并不会把原始五元组完整转给后端。如果后端一定要拿到客户端的源端口需要额外加一句proxy_set_header X-Real-Port $remote_port;否则后端只能看到 Nginx 与客户端建立连接时自己的 443 端口以及 Nginx 与后端连接时使用的随机源端口无法还原真实五元组。这个配置不常用但真遇到了往往会卡很久。3.4 Docker 部署 Nginx 时证书目录怎么挂载用 Docker 部署 Nginx 来跑 HTTPS 也很常见热词里专门提到docker pull nginx:1.21.5、docker 部署 nginx。核心思路是把宿主机里的证书目录和配置文件挂载进容器而不是把证书直接 copy 进镜像。这样证书到期更新时直接替换宿主机文件再 reload 容器即可。一个最直接的方式docker run -d --name nginx-ssl \ -p 443:443 \ -p 80:80 \ -v /etc/nginx/ssl:/etc/nginx/ssl:ro \ -v /path/nginx.conf:/etc/nginx/nginx.conf:ro \ nginx:1.21.5如果是 docker-compose可以这样写services: nginx: image: nginx:1.21.5 container_name: nginx-ssl ports: - 80:80 - 443:443 volumes: - /etc/nginx/ssl:/etc/nginx/ssl:ro - /path/conf.d:/etc/nginx/conf.d:ro restart: unless-stopped挂载时注意证书目录用:ro只读挂载避免容器内意外修改。宿主机的配置文件改动后容器内的 Nginx 不会自动加载。要么docker exec nginx-ssl nginx -s reload要么直接docker restart nginx-ssl。如果证书文件放在挂载目录后Nginx 启动时报权限拒绝注意宿主机目录权限要让运行 Docker 的用户可读SELinux 环境下可能还需要加:Z标签。3.5 端口号、HTTP 强制跳转、日志路径一起搞定Nginx 默认 443 端口跑 HTTPS但有时候内网环境或者端口被占用需要换成 8443 之类的端口。Nginx 配置很简单server { listen 8443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/example.fullchain.pem; ssl_certificate_key /etc/nginx/ssl/example.key; ... }但有个容易忽略的地方改完端口后无论是firewalld还是云平台安全组都要放行对应端口。Windows 环境部署 Nginx 时如果 443 被其他程序占用也可以换个端口来规避。另外如果用了iptables做端口转发也要同步修改。服务一般还是保留 80 端口做 HTTP 强制跳转server { listen 80; server_name example.com; return 301 https://$host$request_uri; }日志方面Nginx 把所有请求和错误分别写到 access_log 和 error_log。排查看得最多的是error.logSSL 握手失败、证书加载失败都会在这里面留下记录server { access_log /var/log/nginx/example.access.log; error_log /var/log/nginx/example.error.log; ... }如果发现客户端一直连不上但证书配置看起来没问题先打开error.log看有没有这类关键字SSL_do_handshake() failedno suitable key sharePEM_read_bio_X509_AUX failed这些基本都能直接定位到问题方向后面章节细讲。4. 排查技巧和常见坑像老运维一样解决问题4.1 证书链不完整导致浏览器/手机客户端警告现象很奇怪电脑 Chrome 能正常打开手机 Safari 或部分浏览器却提示“证书无效”或者后端 API 调用报错说unable to get local issuer certificate。这大概率是证书链没发完整。用下面的命令看一眼服务器下发的证书链openssl s_client -connect example.com:443 -servername example.com -showcerts输出里如果有BEGIN CERTIFICATE累积多段说明完整如果只显示一段说明缺中间证书。解决办法就是把中间证书追加到ssl_certificate指定的文件里顺序还是“叶证书在前中间证书在后”cat server.crt intermediate.crt fullchain.pem改完重载 Nginx再执行上面那条openssl s_client确认返回多段证书即可。4.2 私钥与证书不匹配报错很直接nginx -t时会出现[emerg] Private key does not match the certificate public key原因基本就是证书和私钥不是同一对。比如临时替换证书时拷错了文件或者从不同服务器的备份里东拼西凑。排查方法我在 2.4 节已经说过这儿不再重复。一个容易忽略的场景是证书没问题、私钥也没问题但私钥文件里包含多把钥匙有些导出工具会混进去Nginx 读取时取到了第一把和证书对不上。这时候需要用 openssl 重新解析并输出单一私钥openssl rsa -in bundled.key -out example.key执行后如果提示 unable to load Private Key说明原文件太乱了不如直接从申请证书时生成的 key 文件里找备份。4.3 “cannot load certificate”与文件编码/权限问题启动 Nginx 或 reload 时经常遇到的报错[emerg] cannot load certificate /etc/nginx/ssl/example.crt: PEM_read_bio_X509_AUX failed这个报错有三个常见原因文件不是 PEM 格式而是 DER 二进制。可以用file example.crt确认。文件里有不可见字符最常见是 Windows 记事本添加的 BOM 头或者行尾从 CRLF 变成了额外字符。Linux 下可以用cat -A example.crt看第一行有没有^M。证书文件被加密或损坏。PEM 文件头部如果出现ENCRYPTED等字样需要用密码解密。还有一个低调的问题文件路径没错、格式也没错但 Nginx worker 进程没有权限读取。私钥文件建议权限设为600证书文件设为644并且确保目录可执行chown root:root /etc/nginx/ssl/*.pem /etc/nginx/ssl/*.key chmod 644 /etc/nginx/ssl/*.pem chmod 600 /etc/nginx/ssl/*.key chmod 755 /etc/nginx/ssl4.4 离线环境没有 SSL 模块的坑在离线环境下安装 Nginx 很容易踩一个隐藏的坑系统自带的老包或者别人给的二进制没有编译进--with-http_ssl_module所有证书配置在nginx -t时直接报错nginx: [emerg] unknown directive ssl遇到这种情况先检查当前 Nginx 是否支持 SSLnginx -V 21 | grep http_ssl_module如果没有任何输出说明编译参数里没有 SSL 模块。解决思路有两个如果用系统包管理器安装提前下载好openssl、openssl-devel等依赖再安装支持 SSL 的 Nginx 模块。如果必须离线编译首先确认离线环境里已经有 OpenSSL 开发库然后在 configure 时显式加上./configure --prefix/usr/local/nginx --with-http_ssl_module --with-http_v2_module make make install离线环境下最容易缺的是各种依赖包建议在能联网的机器上先yumdownloader或apt download把所有依赖装齐再迁移到目标机器。这个准备过程很痛苦但一旦漏了openssl-devel编译时就会卡在找不到ssl.h的地方。4.5 “crt”搜索歧义SecureCRT 与证书别搞混最后想专门提一个很接地气的坑热词里大量出现“crt 下载”、“crt 安装”、“crt 连接交换机”、“crt tab”等这些说的其实是 SecureCRT——一个 Windows 平台常用的 SSH 终端客户端工具和 Nginx 的.crt证书文件完全是两回事。SecureCRT 确实也会在配置目录里产生同名文件但那个不是证书。如果你在搜索“Nginx crt 下载”请务必定睛看后缀。Nginx 要的.crt是-----BEGIN CERTIFICATE-----开头的文本文件SecureCRT 是 exe/zip 安装包别下载错了。同样的道理Windows 系统里双击.crt文件有时会弹出“安装证书”向导那是导入系统证书库的操作而 Nginx 服务器上的证书文件不需要双击导入只要放到服务器路径并配置好ssl_certificate即可。4.6 顺手提一下 Nginx 与 OpenSSL 版本的漏洞更新最后再多说一句SSL 证书配置得再漂亮如果底层 Nginx 或者 OpenSSL 有安全漏洞前面都是白搭。运维人在处理完证书后不要忘了看官方安全公告及时把 Nginx 升到稳定版本别让自己的服务一直跑在已知 CVE 漏洞上。升级前先用nginx -V记录当前版本和编译参数升级完再跑一遍配置检查防止新版本默认行为变化导致证书失效。我自己现在处理证书的习惯已经固定成一条流水线拿到 CA 下发的文件后用openssl x509 -text看一眼有效期和 SAN紧接着校验私钥匹配然后合并成 fullchain.pem文件名带上域名和日期放到/etc/nginx/ssl/下改配置、nginx -t、reload最后用openssl s_client验证线上返回的证书链。这套流程看着繁琐但能挡住九成以上和证书相关的线上故障。希望这篇关于.pem和.crt的实战笔记也能帮你形成一套自己的检查清单。
返回列表