如果你有一台服务器想让网站从http变成https整个过程并没有想象中那么复杂。我先后在几台服务器上搭过 HTTPS 服务从什么都不懂到后来能批量签发证书最大体会是先把最基础的自签名证书流程跑通后面换公网证书只是换一个步骤的事。这篇我会从一个最简单的角度出发带你用 Nginx 把 HTTPS 服务完整地搭起来包括证书生成、服务配置、跳转规则以及我踩过的那些坑。整篇文章不依赖任何图形化面板只要服务器上装了 Nginx 和 OpenSSL就能一路跟下来。适合第一次动手的开发者也适合想搞明白证书原理的运维朋友。1. 为什么需要 HTTPS加密之外的那些事1.1 从 HTTP 到 HTTPS协议层面发生了什么HTTP 传输的是明文这句话听起来很简单但实际影响很多人没细想过。你在网页里输入账号密码、支付信息、聊天内容只要路径上有人做了流量抓取这些数据就相当于直接摊在桌面上。更麻烦的是明文传输不止泄露内容连内容是否被篡改都发现不了。第三方完全可以截下请求改掉里面某个数值再放出去服务端和客户端都浑然不觉。这种感觉就像寄明信片沿途经过的每个人都能看到卡片上写了什么还能顺手帮你改几个字。HTTPS 做的就是把这张明信片装进一个只有收发双方能打开的信封里。它的本质是HTTP TLSTLS 负责在 TCP 层之上提供加密、身份验证和数据完整性校验。简单描述一下握手过程客户端先发送ClientHello告诉服务器它支持哪些 TLS 版本和加密套件服务器回复ServerHello带着自己的证书和选定的加密参数客户端验证证书再生成一组用于本次会话的临时密钥之后双方就用这组密钥进行对称加密通信。这里有个容易被忽略的细节HTTPS 不是每一条数据都用公钥去加密那样性能扛不住。实际做法是用证书公钥加密一个临时的会话密钥后续通信全部走高效的对称加密。所以证书的作用更像一把钥匙门打开之后真正的保护由另一套机制来完成。为什么要强调这一点因为实际排查问题时最常见的错误就是把“证书”和“加密”混为一谈。证书只是握手阶段用来交换信任和协商密钥的载体真正的数据加密会话有自己的生命周期。明白了这个分层你后面看到配置里的ssl_certificate、ssl_certificate_key就不会发怵了。1.2 证书的三层结构公钥、私钥与信任链证书不是一张简单的“证明文件”它里面装着一对密钥的公开部分以及一堆元信息。服务器上必须保留私钥文件这个文件一旦泄露等于把信封的开口也给了别人。公开部分则写进证书文件包含域名、签发者、有效期、公钥内容。浏览器为什么信任某个网站不是因为它“有证书”而是因为它的证书能够追溯到浏览器内置的根证书。这个信任链条通常是三层根证书机构中间证书你的服务器证书。根证书是浏览器出厂时预置的中间证书由根证书签发你的证书又由中间证书签发。只要链条完整浏览器就能一层层验证上去确认这个证书确实是某个受信任机构签发的而不是随便一个程序生成的。自签名证书恰恰断掉了这条信任链。你自己给自己签了一张证书浏览器无法从它追溯到任何根证书于是直接弹红屏警告。但在本地开发、内网测试的场合这份警告完全可以接受功能上它照样能提供加密传输。你可以把它理解成自己刻了一枚公章盖在自己写的工作证明上——单位内部认但外单位不认。理解这层结构之后后面生成证书、配置 Nginx、排查“浏览器不信任”这类问题时思路会清晰很多。2. 搭建前的准备域名、服务器与端口规划2.1 先想清楚访问方式域名还是 IP这是我在帮人排查时最先问的问题因为很多人卡在这一步还不自知。如果你的用户是通过https://192.168.1.10这种 IP 地址访问那证书里签的名字也必须能对上这个 IP。自签名证书生成时CN和SAN字段可以用 IP 地址也可以写域名关键是和你浏览器地址栏里输入的内容一致。如果你有计划用一个域名来访问那就需要提前把域名解析到服务器 IP或者在本地测试时把/etc/hosts文件加一行映射把域名指到127.0.0.1。真实公网场景必须做 DNS 解析后面申请受信任证书时也要靠域名验证归属权。我个人的建议是无论你是本地测试还是线上部署尽量统一用域名访问哪怕先在 hosts 里写死映射。因为后续你想从自签名证书换成公网证书域名访问的方式完全不用改动程序只需要替换证书文件。反过来如果你一开始全用 IP后面真要上域名时证书要重新签配置里的server_name也要改。还有一个前期容易忽略的点服务器时间。当初次生成证书和验证证书时本机时间如果偏差过大浏览器会判定证书未生效或者已过期。我之前在一台重启后时间漂移的服务器上折腾了很久后来date -s同步完时间红色警告立刻消失。2.2 端口与防火墙80 和 443 的开放方式HTTPS 服务默认监听 443 端口HTTP 是 80 端口。这个“默认”对用户影响不大因为浏览器会自动帮你把 443 补上但对服务器配置影响很大。首先如果你用非 root 用户启动 Nginx监听 443 端口会遇到权限问题因为 1024 以内的端口默认只有 root 能绑。解决方式很多最稳妥还是让 Nginx 的 master 进程以 root 启动worker 进程降权运行或者直接给二进制文件加cap_net_bind_service能力但这会让后续维护变得麻烦不建议新手一上来就搞。其次云服务器的安全组和操作系统自带的防火墙是两个层级都要放行。我遇到过很多次Nginx 配置看起来完全正确systemctl status nginx也是 running但外部就是访问不了最后发现是安全组没放行 443 入方向。排查顺序建议是这样的先看本机监听再放行本地防火墙最后检查云平台安全组规则一层层排除。还有个容易被忽略的规划问题如果服务器上已经有别的服务占用了 80 或 443比如你在 443 上跑着一个旧的 HTTPS 服务再想上新项目就会冲突。此时要么让 Nginx 接管 443通过反代把流量分发到不同应用要么给新服务换一个高位端口比如 8443用户访问时就得显式写明端口。测试阶段图省事可以直接用 8443但正式对外服务尽量统一用标准端口用户的体验差别还是很大的。3. 核心实操用 Nginx 搭建一个 HTTPS 服务3.1 先生成一张自签名证书OpenSSL 命令逐个参数讲清楚搭建 HTTPS 服务的第一步是让服务器至少有一对密钥和一张证书。先用自签名把整个流程跑通后面换公网证书只是换文件。我用的是 OpenSSL 工具几乎所有 Linux 发行版都自带没有的话通过系统包管理器装一下就行。先创建证书存放目录sudo mkdir -p /etc/nginx/ssl然后执行生成命令sudo openssl req -x509 -nodes -newkey rsa:2048 \ -keyout /etc/nginx/ssl/server.key \ -out /etc/nginx/ssl/server.crt \ -days 365 \ -subj /CNyourdomain.com逐个解释关键参数这些不是摆设req证书请求和生成工具的子命令。-x509直接生成自签名证书而不是生成证书签名请求。少了这个参数你只得到一个 CSR 文件没法直接给 Nginx 用。-nodesNo DES的意思即不给私钥文件加密码口令。如果你不加这个参数OpenSSL 会在生成时让你设置一个密码之后每次 Nginx 启动都要手动输入非常麻烦所以测试环境通常都要加。-newkey rsa:2048生成新的 RSA 密钥对长度 2048 位。这个长度是当前兼容性和安全性都合适的选择1024 偏弱4096 性能开销偏大。-keyout私钥文件的输出路径。-out证书文件的输出路径。-days 365证书有效期自签名证书按需设置一年是常见值。-subj证书的持有人信息。CN字段必须写你的域名或 IP否则浏览器会明确提示证书名不匹配。生成完成之后检查文件权限sudo chmod 600 /etc/nginx/ssl/server.key sudo chmod 644 /etc/nginx/ssl/server.crt私钥权限必须是 600 甚至更紧因为任何能读到这个文件的进程都等价于掌握了你网站的加密身份。证书文件是公开内容权限可以放宽松一些。我见过不少人在这一步图省事直接在项目目录执行命令生成证书然后 Nginx 配置也指向项目目录。这样不是不行问题是混在代码仓库里私钥很容易就被提交到代码托管平台这种低级错误一旦发生服务器等于裸奔。所以证书文件放/etc/nginx/ssl这类系统目录别跟业务代码放一起。3.2 配置 Nginx 支持 HTTPS证书生成好了接下来把 Nginx 配置成监听 443 端口。我这里的示例用的是新版本的 Nginx 配置语法ssl指令直接写在listen参数里。在/etc/nginx/conf.d/下新建一个配置文件名字随意比如https-server.confserver { listen 443 ssl; http2 on; server_name yourdomain.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_protocols TLSv1.2 TLSv1.3; root /var/www/html; index index.html; }核心配置就几个listen 443 ssl让 Nginx 以 HTTPS 模式监听ssl_certificate指定证书路径ssl_certificate_key指定私钥路径。ssl_protocols限制允许的 TLS 版本至少保留 TLSv1.2TLSv1.0 和 TLSv1.1 已经被普遍视为过时脆弱协议不建议再开。改完配置先校验语法sudo nginx -t这一步不能省它会在重载前帮你抓出拼写错误、路径错误、缺少分号等问题。确认输出syntax is ok之后再执行sudo systemctl reload nginx用reload而不是restart可以让正在处理的旧连接优雅结束新连接才切换到新配置生产环境里应该养成这个习惯。启动之后先在服务器本地验证一下openssl s_client -connect 127.0.0.1:443 -servername yourdomain.com这个命令能直接查看证书链、加密套件协商结果如果证书路径配置错了它会在握手阶段直接报错。能看到Verify return code是self signed certificate就说明 HTTPS 链路已经通了只不过这张证书没被浏览器信任。浏览器访问的话地址栏会显示“不安全”或“您的连接不是私密连接”需要手动点击“继续前往”。这是自签名证书的正常表现不用慌说明你的 HTTPS 服务确实在工作。3.3 让 HTTP 流量自动跳到 HTTPS服务搭好之后还有一个用户体验层面的问题很多用户习惯直接敲域名不带https://前缀浏览器默认会走 80 端口。如果 80 端口没做处理用户看到的就是一个空白或者报错的页面体验很糟糕。解决办法是在同一个 Nginx 配置里加一个 80 端口转发server { listen 80; server_name yourdomain.com; return 301 https://$host$request_uri; }这段配置的含义是任何通过 HTTP 访问的请求直接返回 301 永久重定向把用户带到对应的 HTTPS 地址。$host是浏览器请求里携带的域名$request_uri保留原来的路径和查询参数这样从http://yourdomain.com/page?id1访问时会被完整跳到https://yourdomain.com/page?id1参数不会丢。301 和 302 的区别这里值得多说一句301 是永久重定向浏览器和搜索引擎会记住这个结果下次直接访问 HTTPS302 是临时重定向每次都要先走一次 HTTP。生产环境用 301 更合适。但如果你还在调试阶段不确定后续是否要回退可以先临时用 302避免浏览器缓存了跳转导致你调试环境里老访问不到 HTTP 页面。另一个常见小坑如果你在测试环境里用 IP 访问比如http://192.168.1.10想让这条也自动跳转上面的配置里server_name可以直接省略或用_它就会充当默认站点把所有未匹配的 HTTP 请求都跳过去。但注意此时 80 端口上只能有这一个 server 块否则会触发 Nginx 的默认站点优先级问题。4. 从自签名升级到受信任的证书4.1 自签名证书的适用边界自签名证书不是“不好的证书”而是“没被第三方背书的证书”。它最大的价值在于让流程快速跑通零成本不依赖任何外部服务。本地开发、测试环境、内网管理系统、临时演示环境这些场景用自签名完全够用加密效果和正规证书并没有本质区别。但它有一个硬伤浏览器不信任客户端程序如果做了严格的证书校验也会直接拒绝连接。一个对外服务的官网、一个给微信小程序调用的后端接口如果还是自签名用户会看到刺眼的警告页很多不懂技术的人会直接关掉页面。另外自签名证书的有效期全由你自己设置过期之后需要手动再签一次没有自动续期的机制所以长期运行的公网服务不适合停留在自签名阶段。之前有人问我可不可以把自签名证书安装到用户电脑的信任区里这样浏览器就不会报警了可以但只适合企业内部统一管理的终端。对公网用户这个方案完全不可行因为你不可能要求每个访客都去改系统信任配置。4.2 用 ACME 协议申请一张免费证书自签名能跑通流程之后把证书换成受信任的技术路径上只多了一步找一个受信任的签发者帮你签发证书。现在主流的免费方案是 ACME 协议它的好处是整个申请、部署、续期流程都可以自动化。ACME 协议的思路是你的服务器上运行一个客户端声明“我想为这个域名申请证书”然后通过某种方式证明你确实拥有这个域名证明通过后签发机构就会把证书文件交给你整个过程可以自动化执行。第一步确认域名必须已经解析到当前这台服务器的公网 IP。ACME 校验时会尝试通过该域名访问你的服务器或者在 DNS 记录里验证你写入的特定值。如果域名解析不对后面所有操作都白搭。第二步安装 ACME 客户端脚本然后执行签发命令。以域名yourdomain.com为例大致的流程是curl https://get.acme.sh | sh acme.sh --issue -d yourdomain.com --webroot /var/www/html安装脚本会自动把客户端放到当前用户目录下签发成功后证书文件会输出到一个指定的目录一般是类似~/.acme.sh/yourdomain.com/的位置。第三步把签发好的证书安装到 Nginx。这里必须注意客户端生成的证书和私钥路径最好复制到/etc/nginx/ssl/下再修改 Nginx 配置指向这些文件而不是直接引用客户端工作目录里的文件。因为客户端工作目录里的文件可能在后续操作中被调整直接引用容易踩到权限或路径变化的坑。第四步设置自动续期。ACME 证书默认有效期是 90 天所以续期必须自动化。客户端脚本一般会自动写入定时任务但你需要在当前用户下执行一下安装命令把续期后重新生成的证书复制到 Nginx 目录顺便 reload Nginx。这步配置好之后基本就不用再手工碰证书了。如果你不想折腾命令行也可以到云服务器的控制台申请免费证书拿到证书文件后下载再手动配置到 Nginx 里。这种方式适合证书数量很少、不需要频繁更换的场景。区别在于 ACME 是全自动控制台是半自动各取所需。5. 常见问题与排查技巧实录5.1 证书与私钥不匹配这是 HTTPS 服务部署时最高频的问题之一最常见的现象是 Nginx reload 不报错但浏览器访问时报错或者openssl s_client直接提示 key 不匹配。判断方法很简单对比证书和私钥的 moduli 指纹openssl x509 -noout -modulus -in /etc/nginx/ssl/server.crt | openssl md5 openssl rsa -noout -modulus -in /etc/nginx/ssl/server.key | openssl md5两条命令输出的 MD5 值如果一致说明证书和私钥是配套的不一致就说明你配置的两个文件根本不是同一对密钥生成出来的。这种情况通常发生在自己重新生成过私钥但没有同步重新签发证书或者下载证书时从不同服务器各取了一个文件。5.2 证书链不完整导致无法信任有时候你用自签名证书没事换成正规证书后反而出现“证书链不完整”的提示。原因很简单你只配置了服务器证书没有配置中间证书。正规签发机构一般会发给你好几个文件一个是你域名的证书一个是中间证书可能还有一个根证书。处理方式是把中间证书和服务器证书拼在一起组成一个 bundle或者单独指定ssl_trusted_certificate。实际配置里最常见的写法ssl_certificate /etc/nginx/ssl/fullchain.crt; ssl_certificate_key /etc/nginx/ssl/server.key;这里的fullchain.crt就是把服务器证书和中间证书按顺序拼接起来的结果。拼接顺序也很关键服务器证书在前中间证书在后不能反。5.3 配置了 443 但无法访问Nginx 配置看着没问题nginx -t也通过了但浏览器就是打不开这种问题八成出在网络层而不是配置层。排查顺序从近到远ss -tlnp | grep 443这条命令查看本机 443 端口是否在监听。如果看不到输出说明 Nginx 没起来或没监听成功如果能看到监听再检查系统防火墙sudo firewall-cmd --list-ports # 或者 sudo ufw status操作系统防火墙放行之后再去云平台控制台看安全组是否放行了 443 端口的入方向流量。我曾经在三台服务器上接连遇到这种情况最后都是安全组的锅不是本地配置的锅。5.4 浏览器仍然提示不安全服务器时间不准是最隐蔽的原因。证书有生效时间和过期时间如果系统时间和真实时间差了几小时甚至几天浏览器会觉得证书还没有生效或者已经过期。先用date查看时间偏差大的话用 NTP 同步。其次检查证书绑定的域名和访问地址是否匹配。证书是给yourdomain.com签的你访问https://192.168.1.10或者https://yourdomain.com浏览器显然会报错。自签名证书生成时CN写错了也是同一个现象。最后检查证书有效期openssl x509 -in /etc/nginx/ssl/server.crt -noout -dates输出里有两个日期分别是不早于和有效期截止时间对照一下当前时间就知道证书是不是过期了。5.5 安全加固与性能小建议HTTPS 服务跑通之后有几个额外的优化点值得顺手做一下。TLS 版本方面只保留 TLSv1.2 和 TLSv1.3老的协议版本不要再开。加密套件方面Nginx 默认配置已经能协商出主流安全的套件不建议新手随便改改错可能直接导致某些老客户端无法访问。会话复用参数值得开一下减少重复握手带来的延迟开销。另外如果你的业务是 API 接口可以设置合适的keepalive_timeout避免频繁新建连接。监控证书过期是长期运维必须做的事。免费证书一般 3 个月到期即使有自动续期也不能完全信任脚本不会出错。最笨也最有效的办法是把证书有效期查询命令写成一个监控脚本每天跑一次发现剩余天数低于阈值就告警。命令行世界的规律是自动化的前提是你先能手工复现流程所以我建议你在接监控脚本之前先手工执行一次续期和 reload确认命令全程无错再交给定时任务去跑。我自己第一次搭 HTTPS 服务时也对着红色警告页怀疑人生后来发现只是服务器时间差了十分钟。每一次踩坑都在提醒我HTTPS 表面上是一堆配置项背后却是一整套信任与加密体系。把自签名证书这条路走通再换成正式证书整个过程就只剩下“替换文件”这一个动作了。这套流程跑顺之后面对再多的站点也只是批量操作的问题。
阅读完成 · 觉得有帮助?