☰
Nginx实战:从安装配置到反向代理、负载均衡与SSL证书
2026/9/30 17:47:57 网站建设 项目流程

我用 nginx 少说也有七八年了,从最早在服务器上手动编译,到后来直接用系统包管理器一把梭,再到给线上入口做反向代理、负载均衡、平滑升级,踩过的坑确实不少,收获也很实在。现在无论接手什么项目,我第一件事基本都是把 nginx 部署好,把它当成流量进出的大门:静态资源由它直接返回,动态请求由它转给后端,所有端口只对 nginx 开放,后端服务全部关在内部网络里。这篇文章就是想把这几年实操里最常用、最容易出错的部分整理出来,从下载安装、基础配置、反向代理,到 SSL 证书、平滑升级和问题排查,按我实际验证过的做法讲一遍,不背官方文档,适合刚接触 Linux 部署的初学者,也适合已经在用但想系统补一遍细节的开发者。

1. 为什么我会建议每个项目都用 nginx

1.1 三个核心使用场景,决定你的学习主线

很多人一上来就背 nginx 配置,背完就忘,原因是没有先想清楚 nginx 到底解决什么问题。我自己的理解是,nginx 的日常使用可以压缩成三件事:第一,托管静态资源,比如前端打包出来的 html、css、js、图片,nginx 处理这类请求非常快,几乎不占什么 CPU;第二,反向代理,把请求转发给后端的 Java、Python、Go 服务,同时在转发过程中补上客户端真实 IP、Host 等头信息;第三,负载均衡,当一个后端撑不住流量时,用 upstream 模块把请求分摊到多台机器上。绝大多数你在网上搜到的教程、面试题、生产运维操作,核心都绕着这三件事展开。

想明白这一点,你的学习顺序就清晰了:先装好 nginx,配一个能访问的静态页面,然后学着配 server 和 location,再去理解 proxy_pass 和 upstream,最后补 SSL 证书和缓存压缩这些优化项。这也是我这篇文章的组织顺序,按真实工作流推进,而不是按官方文档的参数顺序堆砌。

1.2 进程模型:master 与 worker 是怎么配合的

理解了进程模型,你以后看日志、做平滑升级、排查“nginx 怎么不生效”这类问题都会轻松很多。nginx 启动后会有两类进程:一个是 master 进程,也叫主进程,它不直接处理请求,只负责读取配置、管理 worker 进程、执行平滑升级和日志切割;另外还有若干 worker 进程,才是真正处理并发请求的干活角色。worker 进程的数量默认等于服务器的 CPU 核心数,每个 worker 用事件驱动的方式同时维护成千上万个连接,这也是 nginx 能扛住高并发的根本原因。

你执行nginx -s reload时,实际上就是 master 进程检查新的配置,然后把 reload 信号发给老 worker,老 worker 处理完手上的请求后优雅退出,新 worker 用新配置拉起。整个过程用户请求基本不中断,所以“平滑”这两个字不是白叫的。如果你不理解这个机制,很容易在改配置后产生“我明明 reload 了为什么没生效”的错觉,实际上可能是你改了文件但语法错误,master 直接拒绝了加载。

1.3 该把 nginx 放在网络架构的哪个位置

我的习惯是,所有对外 HTTP/HTTPS 流量都必须经过 nginx,后端服务一律监听内网地址或者只监听本机回环地址。这样做有几个直接的好处:一是端口暴露面大幅缩小,不会被扫描器乱打;二是可以在入口层统一做 HTTPS 终止、Gzip 压缩、请求限速、访问控制;三是后端服务后续扩容时,只需要改 nginx 的 upstream,不需要动客户端配置。

之前接过一个老项目,Tomcat 直接监听公网 8080 端口,一天能被扫描器打几千次。后来我在前面加了一层 nginx,对外只开放 80 和 443,Tomcat 改成监听 127.0.0.1:8080,配合防火墙规则把外部访问全拦掉,服务器立刻安静了。这个教训也让我养成了一个习惯:每次部署新服务,第一件事是想清楚它在链路里的位置,而不是先急着启动进程。

2. 安装 nginx:从下载到平滑升级一篇学会

2.1 官方仓库、源码编译、系统包管理器,该选哪种

安装 nginx 至少有三种常见方式,不同场景选择不同。第一种是直接用系统的包管理器安装,比如 Ubuntu 上的apt install nginx、CentOS 上的yum install nginx,这是最省事的,适合绝大多数开发环境和一般生产环境,好处是后续可以用系统的systemctl统一管理,也能跟随系统源自动更新。第二种是从 nginx 官网下载编译好的二进制包,适合想要最新主线版本、又不想自己编译的场景。第三种是源码编译安装,适合需要自定义模块、或者目标服务器断网隔离、必须手动指定依赖的场景。

对于刚入门的人,我建议直接从包管理器开始,先把主流程跑通。对于生产环境,如果发行版仓库里的版本太老,比如某些 CentOS 默认源里的 nginx 版本已经落后好几个大版本,那就考虑用官方提供的 nginx 仓库源来安装。至于源码编译,除非你有明确的模块定制需求,否则付出的时间成本不太值,官方和第三方 Pre-built 包通常够用。

2.2 Linux 安装实操,Ubuntu 与 CentOS 都覆盖

在 Ubuntu 20.04 或 22.04 上,安装 nginx 只需要两三条命令:

sudo apt update sudo apt install -y nginx systemctl status nginx

装完之后 Ubuntu 会自动把服务拉起,默认监听 80 端口。你可以直接用浏览器访问服务器的 IP,看到 Welcome to nginx 页面就说明成功了。配置文件分布在/etc/nginx/目录下,主配置是/etc/nginx/nginx.conf,站点配置通常放在/etc/nginx/sites-available/和/etc/nginx/sites-enabled/,这个设计是为了方便用软链接启用和停用站点,和 CentOS 的习惯不太一样。

CentOS 7 上稍微有点门槛,因为默认源里没有 nginx,需要先装 EPEL 源才能用yum install nginx:

sudo yum install -y epel-release sudo yum install -y nginx sudo systemctl enable nginx sudo systemctl start nginx

CentOS 的配置目录是/etc/nginx/conf.d/,所有以.conf结尾的文件都会被主配置 include 进来。如果你嫌弃系统源版本太老,也可以配置 nginx 官方源再安装,但这个操作要小心,不同大版本的官方源地址不一样,装完一定要执行nginx -v确认版本符合预期。

另外提一句离线安装的场景。有些内网服务器或者信创环境,比如麒麟系统,没法直接访问外网源。我的做法是在一台同版本、同架构的联网机器上,用apt download或yumdownloader把 nginx 及其依赖包全部下载下来,再拷贝到内网机器上用dpkg -i或rpm -ivh依次安装。这样能绕开在线源的依赖解析问题,但是依赖顺序要理清楚,装的时候如果报缺依赖,就对照错误提示逐个补包。源码编译在这种场景其实也可以,但需要提前准备 gcc、make、pcre-devel、zlib-devel、openssl-devel 这些工具链,打包搬运的工程量更大,我一般只在内网实在找不到对应 rpm 包时才走编译。

2.3 Windows 上安装 nginx 的注意事项

Windows 上安装 nginx 就简单很多了,去官网下载 Windows 版本的 zip 包,解压就能用,不需要安装程序。解压目录建议别放中文路径,也别放带空格的路径,否则某些模块处理文件路径时容易出幺蛾子。运行方式是双击nginx.exe,或者到解压目录执行:

cd C:\nginx-1.26.2 start nginx

启动后浏览器访问 localhost,能看到欢迎页就说明起来了。这里容易踩的坑是 80 端口被 IIS 或其他程序占用,如果启动没反应,先执行netstat -ano | findstr :80看看端口被谁占了。Windows 版本主要用来本地模拟配置、验证规则,不太适合跑重型生产流量,因为 Windows 上的 nginx 性能和 Linux 版有明显差距,而且不推荐用 Windows 做反向代理服务器。

Windows 上重载配置的命令是nginx.exe -s reload,停止是nginx.exe -s stop。如果改了配置之后想验证语法,用nginx.exe -t,它会告诉你配置文件里有没有语法错误,这是所有平台都应该养成的习惯。

2.4 平滑升级与版本回滚

说到平滑升级,很多人以为nginx -s reload就叫平滑升级,其实不对。reload 只是重载配置文件,二进制版本没变。真正的平滑升级,是你在不中断服务的前提下,把旧的 nginx 二进制换成新的版本。标准流程是这样的:先下载新版本源码或二进制,编译好之后,把旧二进制备份一下,再把新二进制替换掉,然后给 master 进程发送 USR2 信号,让它启动新的 worker;等新 worker 正常接管流量后,再向旧 master 发送 WINCH 信号,让旧 worker 优雅退出。

实际生产中我更推荐一种更省心、也更少出错的做法:用nginx官方包或发行版仓库直接升级,比如 Ubuntu 上apt upgrade nginx,升级过程中包管理器本身就会处理好二进制替换和重启流程,配合systemctl reload nginx通常能做到秒级平滑。如果升级后发现问题需要回滚,最稳妥的方案是提前备份旧版本的二进制和配置文件,把备份文件恢复回去再 reload。这个流程在医院、银行等不允许服务中断的场景里经常用到,我只能给一个建议:升级前一定跑一次nginx -t,并且至少保留上一个版本的完整备份目录,别嫌麻烦。

3. 配置文件才是 nginx 的灵魂

3.1 nginx.conf 的主干结构

nginx 配置看起来层级很多,实际上主干就四个块:events块负责全局事件模型配置,比如 worker 连接数;http块负责 HTTP 相关配置,里面可以写server;server块表示一个虚拟主机,匹配域名和端口;location块再往下细粒度地匹配 URL 路径。一个最精简的配置长这样:

events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name example.com; location / { root /var/www/html; index index.html; } } }

这里要注意location /里的root和alias的区别,这是新手最容易搞混的一组概念。root是拼接式的,请求/images/a.png时会去找/var/www/html/images/a.png;而alias是替换式的,如果写成alias /data/files/,请求/images/a.png会找/data/files/a.png。我经常看到有人用root配一个下载目录,结果路径多了一层,访问全部 404,其实就是这里的拼接逻辑没想清楚。

3.2 server_name 实战:主域名与二级域名一个入口搞定

一台服务器上配多个站点是 nginx 最常见的需求,比如主域名example.com对应官网,二级域名blog.example.com对应博客,api.example.com对应后端接口。实现方式就是在同一个 http 块里写多个 server 块,nginx 根据请求头里的 Host 字段进行匹配。基本写法如下:

server { listen 80; server_name example.com www.example.com; root /var/www/site; } server { listen 80; server_name blog.example.com; root /var/www/blog; } server { listen 80; server_name api.example.com; location / { proxy_pass http://127.0.0.1:8080; } }

有几件事我是在生产环境里吃了亏之后才明白的。第一,DNS 解析必须先把这些域名都指向服务器 IP,否则 nginx 配置再对也没用。第二,如果有人直接用 IP 访问,而你没有给 IP 配 server 块,nginx 会默认用第一个 server 块来响应,这通常会引发奇怪的问题,所以我会习惯性地加一个默认拒掉的 server 块:

server { listen 80 default_server; server_name _; return 444; }

return 444是 nginx 的特色写法,服务器直接关闭连接,不返回任何响应体,很多人在实测之后都喜欢这个方案。第三,如果同一域名不同端口、同一端口不同域名需要不同类型处理,比如 HTTP 跳 HTTPS、首页重定向到二级目录,都用 server 块配合return或rewrite实现,别在 location 里写太多重定向逻辑,否则排查起来会非常痛苦。

3.3 反向代理的核心写法与 proxy_pass 的坑

反向代理是 nginx 使用频率最高的功能,而且我敢说至少有一半的配置问题是出在proxy_pass后面的路径上。看这个例子:

server { listen 80; server_name api.example.com; location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }

注意这里的proxy_pass结尾带不带斜杠,效果完全不同。如果写成proxy_pass http://127.0.0.1:8080;不带斜杠,请求/api/user会原样转发给后端,后端收到/api/user,由后端的路由去处理/api前缀。如果写成proxy_pass http://127.0.0.1:8080/;带斜杠,那么 location 里匹配到的/api/前缀会被替换掉,请求/api/user转发给后端时变成/user。这个前缀替换逻辑非常容易踩坑,特别是前后端约定不一致的时候,你可能会看到 404、重定向丢失或者接口路径错误,第一反应往往是怀疑后端代码,实际上问题出在 nginx 这一层。

另外提醒一下,反向代理场景中proxy_set_header这几个头字段不要省,否则后端拿不到真实的客户端 IP,尤其是做日志分析、限流、风控的时候,$remote_addr永远只是 nginx 服务器的地址,真正的客户端地址要靠X-Forwarded-For来传递。如果你代理的是 WebSocket 或者长连接服务,还要额外加Upgrade和Connection头,并且把超时时间调大。

3.4 upstream 负载均衡参数选择

当后端服务有多台实例时,就需要把 upstream 块和 proxy_pass 配合使用。upstream 是定义在 http 块里的一个服务组,里面列出后端机器的 IP 和端口:

upstream backend { server 192.168.1.10:8080 weight=2; server 192.168.1.11:8080 weight=1; keepalive 32; } server { listen 80; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } }

默认情况下 nginx 使用轮询算法,每个请求按顺序轮流发给后端机器。加上weight参数可以分配权重,适合两台机器配置不同、性能不均的场景。还有几种常用策略:ip_hash能保证同一个 IP 的请求始终落在同一台后端上,适合有 session 但没有做分布式 Session 共享的旧项目;least_conn会把请求发给当前连接数最少的那台机器,适合请求处理时间差异很大的场景。

这里有个隐藏点容易被忽略:nginx 默认与后端交互用的是 HTTP/1.0,而 HTTP/1.0 不支持连接复用,每个请求都要重新建立 TCP 连接,在高并发场景下会浪费大量握手开销。所以我在生产环境里都习惯加上keepalive 32和proxy_http_version 1.1,让 nginx 与后端之间维持一批长连接,实测对 QPS 提升非常明显。

3.5 结合 Tomcat:nginx 正确地和 Java 应用配合

很多人在网上搜“linux 部署 tomcat nginx”,核心问题就是:nginx 到底能不能跑 JSP?答案很明确,nginx 本身不支持 JSP 解析,它的强项是静态文件、反向代理,而 JSP 的解析和执行需要交给 Tomcat 这样的 Servlet 容器。所以标准的架构是:nginx 监听外部端口,把动态请求转给 Tomcat,同时静态资源由 nginx 直接返回。

一个典型配置长这样:

server { listen 80; server_name app.example.com; location / { proxy_pass http://127.0.0.1:8080; 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 ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2)$ { root /opt/tomcat/webapps/ROOT; expires 7d; } }

动态请求全部交给proxy_pass,静态资源请求走正则 location 直接读文件。这样做的效果是,Tomcat 只需要专心处理 Servlet 和 JSP,静态资源不占用它的线程池,对高并发场景帮助很大。还有一点值得注意:Tomcat 在 nginx 后面时,很多应用会用到request.getScheme()或request.isSecure()来判断当前请求是 HTTP 还是 HTTPS,如果 nginx 做了 SSL 终止,Tomcat 收到的是普通 HTTP 请求,就可能生成错误的重定向链接。这种问题通常靠proxy_set_header X-Forwarded-Proto $scheme;加上 Tomcat 的 RemoteIpValve 配置解决,如果你部署的应用出现“页面访问是 HTTPS、但是里面重定向后变成 HTTP”的怪现象,优先往这个方向查。

4. 生产环境里一定要会的配置细节

4.1 静态资源缓存与 gzip 压缩

优化性能最先做的两件事就是缓存和压缩。先说缓存,浏览器请求静态资源时,nginx 通过expires或Cache-Control告诉浏览器这个文件可以缓存多久。比如图片和 CSS 这类基本不变化的资源,缓存一周完全没问题:

location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2)$ { expires 7d; add_header Cache-Control "public, max-age=604800"; }

这里有个搭配技巧,如果前端发布时给文件名加了 hash,比如app.8f3k2.js,缓存时间可以放心放得很长;如果没有 hash,缓存太久会导致用户更新后还在用旧文件。所以我在实际项目里都建议前端同学在打包时加上 hash 后缀,这样 nginx 这里就可以无脑设置长缓存。

再说 gzip,同样是减少传输体积、提高加载速度的手段。开启方式是在 http 块里加上:

gzip on; gzip_comp_level 5; gzip_min_length 1k; gzip_types text/plain text/css application/javascript application/json application/xml image/svg+xml;

gzip_comp_level不是越高越好,级别高压缩率提升有限,但 CPU 消耗明显增加,实测 5 左右是性价比比较高的档位。gzip_min_length 1k表示小于 1KB 的文件不压缩,因为压缩这类小文件带来的收益微乎其微,反而白白浪费 CPU。还有一个很容易漏的配置是,如果走的是反向代理,且后端返回的响应头里带Content-Length而不是Transfer-Encoding: chunked,nginx 压缩会受影响,所以生产环境我会把gzip_http_version 1.1也写上,细节不一定马上用得上,但踩过一次坑之后就明白为什么要写。

4.2 共享文件和下载目录怎么暴露才安全

“nginx 共享文件”是网上高频搜索词,通常是团队内部需要把某台机器上的目录通过 HTTP 方式共享给同事下载。使用 alias 配置一个纯下载站点,一行就能解决:

server { listen 8088; server_name _; location /download/ { alias /data/share/; autoindex on; autoindex_exact_size off; autoindex_localtime on; } }

autoindex on是打开目录列表功能,访问时可以看到整个目录的文件列表,autoindex_exact_size off让文件大小显示为可读格式,autoindex_localtime on让文件时间显示成本地时间。这个配置做内部文件共享确实方便,但直接暴露在公网会非常危险,容易变成别人抓取数据的免费网盘。所以我通常会在前面加一层访问限制,比如最简单的 HTTP Basic 认证,或者用allow和deny限制来源 IP。

location /download/ { alias /data/share/; autoindex on; auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd; }

.htpasswd文件可以用openssl passwd -apr1生成密码散列,再手工拼成“用户名:散列值”的格式。虽然这种方式不算最安全,但对付内部工具站已经足够了。更重要的一点是,共享目录千万别放在 nginx 的 web 根目录下面,否则别人可能通过拼接路径访问到目录外的文件,alias 的配置一定要注意路径结尾的斜杠对应关系。

4.3 mirror 请求复制、超时控制与限速

网上搜“nginx mirror 超时时间”,通常指向两个方向:一是 nginx 的 mirror 模块,它能把线上真实流量复制一份到另一个地址,常见用途是做流量回放、日志采集、灰度对比;二是 nginx 向上游发起请求时的各种超时时间设置。这两个我分开说。

mirror 模块的用法是在 location 里指定一个 internal 的内部 location,nginx 收到请求后会同时向 mirror 地址发一份复制流量,但不会等待 mirror 的响应,主请求仍按原路径正常返回。这个特性很适合做新老系统对比或者录制线上流量用于测试:

location /api { mirror /mirror; proxy_pass http://backend; } location = /mirror { internal; proxy_pass http://mirror-backend; }

注意mirror一定要放在主 location 里,internal表示这个地址外部访问不到,只能被 nginx 内部调用。mirror 毕竟是额外请求,如果 mirror 后端响应很慢,可能会拖累 worker 进程的资源占用,所以网上问 mirror 超时时间怎么办,通常不是调快 mirror 本身的返回,而是要给 mirror 的 location 单独设proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout,让复制请求在超时后及时断开,不影响主流程。

和超时相关的几个参数在反向代理里也经常调整:proxy_connect_timeout默认 60 秒,指建立后端连接的超时;proxy_read_timeout默认 60 秒,指两次读取数据之间的最大间隔;proxy_send_timeout默认 60 秒,指发送数据给后端的超时。如果你的应用里有长轮询、大文件上传、或者后端接口本身执行时间较长,这几个值不调大,用户就会遇到莫名的 504,而 nginx 错误日志里只显示“upstream timed out”,非常容易误判成后端挂掉。

4.4 用 VSCode 把 nginx 配置格式化

nginx 的配置文件不像代码,默认没有很严格的缩进风格,多人协作时经常发现每个人写的格式都不一样。我在实际团队里推广过一套方案,用 VSCode 来做 nginx 配置的编辑和格式化,效果还不错。具体做法是:先在 VSCode 里安装一个名为nginx-formatter的插件,它基于 nginx 官方提供的格式化工具,然后打开任意.conf文件,右键选“格式化文档”或者用快捷键Shift+Alt+F,插件会自动把缩进、空格、换行统一成规范格式。

还有一个更推荐的做法:不依赖 VSCode 插件,而是在配置里养成写好分号、不嵌套过深的习惯。再配合nginx -t做语法检查,基本就能避免大部分低级错误。另外 VSCode 的 nginx 插件会提供$host、$remote_addr这类变量的自动补全和高亮,对新手友好很多,不熟悉内置变量的人可以靠提示快速上手。注意插件格式化之后一定要重新跑一遍nginx -t,因为格式化不会改变语义,但如果有注释位置特殊或者多行参数,不同版本插件处理方式可能有细微差异,语法检查最终兜底。

4.5 SSL 证书申请与配置,少走弯路的姿势

给站点加 HTTPS 是现代部署的基本要求。证书来源主要有两类:一类是付费或免费的 DV 证书,很多云厂商提供免费一年证书,下载时选择 nginx 类型,会得到一个.crt/.pem证书文件和一个.key私钥文件;另一类是 Let’s Encrypt 这类免费自动化证书,通过 ACME 协议自动申请和续期。

下载到的证书放到 nginx 配置里,核心写法是:

server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/certs/example.com.crt; ssl_certificate_key /etc/nginx/certs/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; }

很多人在这一步遇到各种奇怪问题,我先说三个最典型的。第一,证书文件路径或权限不对,nginx 启动时报“cannot load certificate”错误,这一般是证书和私钥文件放到了 nginx 用户读不了的目录,或者文件格式不对,解决方法是确认权限为 600 或 644,并用openssl x509 -in 证书文件 -noout -text检查证书内容。第二,证书和私钥不匹配,nginx 启动时也会报错,可以用openssl x509 -noout -modulus -in 证书文件和openssl rsa -noout -modulus -in 私钥文件分别计算 modulus,两个输出一致才说明匹配。第三,配好 443 之后别忘了把 80 端口的重定向加上,最简单的写法是:

server { listen 80; server_name example.com; return 301 https://$host$request_uri; }

有些用户申请的是云厂商提供的免费证书,下载时明明选了 nginx 类型,只有一个.crt和一个.key,但浏览器还是不认,这种大概率是证书链不完整。解决办法是把网站证书和中间证书合并成一个.crt文件,顺序是网站证书在前、中间证书在后,合并之后重新加载 nginxnginx -s reload再测就正常了。

5. 常见问题排查与卸载实录

5.1 日志会说话,先学会怎么看日志

遇到 nginx 问题,第一件事不是猜,而是看日志。nginx 有两个日志:access.log 记录每次请求的访问信息,error.log 记录启动错误、配置错误和转发错误。Ubuntu 上日志位置通常在/var/log/nginx/,CentOS 也一样。我排查问题时的顺序很固定:先tail -f /var/log/nginx/error.log看有没有新报错,如果访问报 502 或 504,去 error.log 里搜 “connect() failed” 或 “upstream timed out”,这两个关键字能直接告诉你后端连接失败还是后端超时。

access.log 里要重点看的字段有状态码、请求耗时和代理地址。比如日志里有大量 499 状态码,说明客户端在 nginx 还没返回结果之前就主动断开连接了,这种现象在高并发或慢接口场景下很正常,但如果你做的是视频上传类应用,499 频繁出现往往意味着超时时间设置不对。养成“报错先看日志”的习惯之后,90% 的问题都能在 5 分钟内定位到方向,而不是像无头苍蝇一样到处改配置。

5.2 “no required ssl certificate was sent” 与其他证书类报错

很多人在 nginx 配置双向 TLS 时(也就是客户端证书验证场景)会看到这么一条错误:no required ssl certificate was sent。这个报错直译是“没有发送需要的 SSL 证书”,意思是 nginx 要求客户端提供证书,但客户端没有传。这通常发生在ssl_verify_client on配置开启了强制客户端证书认证时,而浏览器或客户端没有安装对应的客户端证书。

处理思路有两个方向:如果你确实需要双向认证,就要先给客户端签发证书并安装到请求方,然后把ssl_client_certificate指向 CA 证书文件;如果你本来只是想配单向 HTTPS,根本不该加ssl_verify_client这行配置,找到并删掉即可。我见过不少人被这个问题困扰很久,最后发现是误拷贝了网上某个 “双向认证” 配置模板,把不需要的验证开关也抄了进来。排查时先grep -r "ssl_verify_client" /etc/nginx/检查所有配置,确认哪里开启了验证,再去判断这个需求是不是真的必要。

5.3 卸载 nginx 时的残留清理

卸载看似简单,但系统里残留的配置、日志、进程会导致你重装 nginx 后遇到“地址已在使用”或端口冲突。Ubuntu 上卸载并清理的完整命令是:

sudo systemctl stop nginx sudo apt remove --purge nginx nginx-common nginx-full sudo apt autoremove

如果之前是源码编译安装的,包管理器根本不知道 nginx 存在,你需要手动停掉进程,然后删除/usr/local/nginx或你当初指定的安装目录,以及/etc/nginx和/var/log/nginx等残留目录。判断是否真正清理干净,用which nginx和ps -ef | grep nginx两条命令检查一下就行。

重装 nginx 时遇到 “bind() to 0.0.0.0:80 failed” 的错误,十有八九是端口被旧进程占用。这时候执行ss -tlnp | grep :80找到占用进程的 PID,然后根据情况停掉或杀掉,再把新装的 nginx 拉起来。这个报错在生产切换时特别常见,别急着改配置,先在系统层面确认端口确实被释放了。

5.4 超时、请求大小、502/504 速查表

我把生产环境里遇到频率最高的几个问题集中整理成了一张表,比较适合直接收藏:

现象常见原因排查方向
502 Bad Gatewaynginx 无法连接后端检查后端进程是否存活,端口是否监听,防火墙是否拦截
504 Gateway Timeout后端响应太慢,触发proxy_read_timeout调大超时,同时排查后端慢查询、线程阻塞
499 状态码客户端在响应前断开检查接口耗时、前端超时设置,或者需要调大 keepalive
413 Request Entity Too Large上传文件超过client_max_body_size在 server 或 location 中调大该参数
404 Not Foundroot/alias 路径配置错误或后端路由缺失先看日志确认请求实际路径,再对比root拼接规则
connection refused后端端口未监听或防火墙拦截ss -tlnp检查监听,telnet 测试连通性

这里重点说一下 502 和 504 的区别:502 是连接建立失败,后端压根没起来或者端口不通;504 是连接建立成功但响应超时,后端可能还活着,只是处理请求太慢。这两个状态一出来,排错方向完全不同的,新手最容易混。还有一个容易被忽略的问题:如果后端的 worker 线程池被慢请求耗尽,新连接排不上队,也会表现为 502/504,这时候光调 nginx 超时没用,必须去后端的线程池、数据库连接池那边找根因。

6. 写在最后:一些靠时间换来的经验

说几条我这些年攒下的实在体会吧。第一,每次改 nginx 配置前,先备份一份,备份文件名带上日期,改完后立刻执行nginx -t验证语法,再 reload。我见过太多人在生产环境手一抖删错一个分号或者写错一个变量名,直接把整个站点搞挂。第二,能用include拆分配置的就拆开,别把所有 server 块堆在一个 nginx.conf 里,我习惯按域名拆成独立的 conf 文件,每个文件只负责一个站点或一组服务,排查问题时互不干扰,谁出的问题看谁的文件。第三,systemctl restart nginx和nginx -s reload是有本质区别的,restart 会先停再启动,连接会断;reload 是平滑重载,连接基本不受影响。所以平时配置变更尽量用 reload,只有改了一些底层参数或者加载新模块时才需要 restart。

还有一个小技巧是学会用curl -I来做验证。比如你改了 SSL 证书,执行curl -I https://example.com看返回的证书信息和状态码;你怀疑路径配置错了,执行curl -I http://127.0.0.1:80/xxx直接看响应。很多时候用一条 curl 命令就能确认问题到底在 nginx 还是后端,比反复开浏览器 F12 要高效得多。

nginx 这东西,说难不难,说简单也不简单,但只要你理解了它的进程模型、location 匹配规则和 proxy_pass 的转发逻辑,后面遇到的大多数问题都是配置细节问题,靠日志和 curl 就能解决。希望这篇总结能帮你在部署和排错时省点时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询