ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Nginx核心功能实战:反向代理、负载均衡、SSL配置与平滑升级

Nginx核心功能实战:反向代理、负载均衡、SSL配置与平滑升级 做后端和运维的同学几乎没人绕得开Nginx。不管是个人博客、企业官网还是日活百万的业务系统Nginx要么挡在最前面接流量要么在内部做转发代理。很多人会配置几个location、会写proxy_pass就觉得自己会Nginx了可真到了高并发压测、多站点上线、证书挂载、平滑升级这些场景坑一个接一个。这篇就围绕Nginx核心功能把反向代理、负载均衡、动静分离、高并发模型、SSL证书、平滑升级这些点串起来过一遍结合我这些年在真实项目里踩过的坑和实验过的方案尽量讲得能直接落地。如果你正准备学Nginx、刚接手一个用Nginx部署的项目或者想系统梳理一下Nginx的难点和面试题考点这篇应该能帮你省不少时间。我不打算堆文档式的命令清单而是按它到底帮你解决什么问题、为什么这么设计、配的时候要注意什么这个思路展开。1. 反向代理Nginx最日常、也最容易出岔子的能力1.1 正向代理与反向代理的分界线很多人一开始搞不清正向代理和反向代理的区别。我习惯打一个比方正向代理是帮你出门办事的跑腿小哥它代替你访问外部资源服务器不知道真正的访问者是谁反向代理则是公司前台访客统一先找前台前台再按来访意图把人引到对应的办公室客户端不需要知道背后的具体工作人员是谁。对应到技术上正向代理客户端配置代理地址代理服务器替客户端去请求目标服务器常见于办公网络管控、爬虫绕过访问限制等场景。反向代理客户端只知道Nginx的地址Nginx根据请求路径、域名把请求转发给后端的应用服务器集群客户端无感知。Nginx最常用的就是反向代理。它接收HTTP请求然后按规则转发给上游upstream服务器再把上游的响应原样返回给客户端。这个过程中还可以顺手做负载均衡、缓存、鉴权、限流、改写请求头等操作。1.2 proxy_pass 的斜杠陷阱末尾 / 决定请求路径我第一次用Nginx反代的时候最疑惑的就是proxy_pass后面到底要不要加斜杠加不加效果完全不一样。这可以说是Nginx反向代理里最高频的坑。先记住一个结论在location里写proxy_pass如果目标地址不带URI就是没有路径部分Nginx会把原始的URI原样转发如果带URI哪怕只有一个/请求中匹配location的那段路径就会被替换掉。举个例子假设配置文件是location /api/ { proxy_pass http://backend; }此时请求/api/user转发给后端的地址依然是/api/user路径原封不动。但如果写成location /api/ { proxy_pass http://backend/; }请求/api/user会被改写成/user后再转发因为location匹配到的/api/被proxy_pass新地址里的/替换掉了。这个特性的坑在于你的前端、后端如果对URL规范要求不严格改动后可能一时半会发现不了问题直到某个接口强校验了路径前缀或者出现了404排查起来非常痛苦。我后来的经验是凡是改proxy_pass一定先写下来原始URI和转发后URI的对照再上测试环境验证。线上出过一次因为少写一个斜杠导致回调地址错了的事从那以后我对这个细节就格外敏感。1.3 上游集群与单机反代的区别如果后端只有一台服务器直接写proxy_pass http://192.168.1.10:8080;就够了。但很多时候后端是Tomcat、Node或者其他语言的服务部署在一个集群里。这时候就需要用upstream定义一组上游服务器upstream backend_pool { server 192.168.1.10:8080 weight2; server 192.168.1.11:8080 weight1; } server { listen 80; server_name example.com; location / { proxy_pass http://backend_pool; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }顺带解决一个常见疑问Nginx不能直接解析JSP。JSP需要Tomcat这类应用服务器处理Nginx扮演的角色就是先把请求反向代理到Tomcat地址上再由Tomcat编译执行JSP最后把HTML返回给Nginx。很多人一上来就问Nginx支不支持JSP准确说法是Nginx本身不支持但通过反向代理可以完美配合JSP应用。proxy_set_header这几行配置也值得关注。后端服务如果依赖客户端IP做日志分析、风控或业务逻辑必须靠这几个header把真实IP透传过去。特别是走了多层代理时X-Forwarded-For会把每层代理的IP追加进去后端要取的就不再是最后一跳的IP而是链路最前面的那个。2. 负载均衡让一堆后端真正均衡地干活2.1 upstream 的四种常用调度算法负载均衡是Nginx最核心的卖点之一。它把客户端请求按照一定策略分发到多台上游服务器实现水平扩展。Nginx默认支持的调度算法有四种理解它们各自的特点才能选对。算法默认特点适用场景轮询round-robin是请求按时间顺序逐一分配到不同服务器后端无状态、性能相近的场景权重weight配合轮询按权重比例分配权重高承担更多请求服务器配置不同新老机器混跑ip_hash否对客户端IP做哈希同一IP固定落到同一台服务器需要会话保持无共享Session的情况least_conn否优先转发给当前活跃连接数最少的服务器请求处理耗时长、长短请求混合的场景权重配置我在实际项目中用得最多。升级机器时新机器的weight先调低观察稳定后再慢慢调高比一次性压流量稳妥得多upstream backend_pool { server 192.168.1.10:8080 weight3; server 192.168.1.11:8080 weight1; # 新上线先少接点流量 }ip_hash也经常被问到。它的作用是让同一个IP的请求总是落到同一台后端解决Session共享问题。注意它依赖客户端的IPv4前三个字节或IPv6整体来做哈希如果客户端通过代理访问所有请求都会集中到代理IP上起不到均匀分布的作用这一点必须心里有数。2.2 异常摘除与恢复被动健康检查的参数博弈Nginx默认有一种被动健康检查机制不是主动去探测后端存活而是转发请求失败后把后端标记为不可用。两个关键参数是max_fails和fail_timeoutupstream backend_pool { server 192.168.1.10:8080 max_fails3 fail_timeout30s; }含义是如果在30秒内转发给这台服务器的请求失败了3次Nginx会在接下来的30秒内认为它不可用不再转发新请求。30秒后会自动恢复尝试如果又连续失败继续摘除。这个机制简单可靠但也藏着坑。max_fails默认值是1也就是说连续1次失败就摘除对于瞬时抖动、连接超时的服务来说过于敏感而fail_timeout同时决定了标记多久和统计多长时间窗口。线上我一般会调成max_fails2 fail_timeout10s既不至于一抖就摘又能快速剔除真正挂掉的节点。要注意的是这种被动检查只对实际转发失败的请求生效如果后端服务还在但已经假死端口通、请求卡住不返回Nginx自己是发现不了的。要真正做到主动健康检查需要用第三方的nginx_upstream_check_module或者商业版的Nginx Plus。小规模项目可以接受被动检查但涉及核心交易链路建议加上主动探测。2.3 keepalive 长连接忽略它等于浪费一半性能upstream里还有一行配置很多人要么不写要么不知道它干嘛用upstream backend_pool { server 192.168.1.10:8080; server 192.168.1.11:8080; keepalive 32; }这个keepalive不是客户端和Nginx之间的连接而是Nginx到后端服务器之间保留的空闲长连接数量。后端每处理完一个HTTP请求连接并不立刻关闭而是放进连接池复用。如果这行不配Nginx每转发一个请求就要和后端重新建立TCP连接遇到高并发时TIME_WAIT状态连接会堆积后端的端口资源也吃紧。这里必须注意一个配套写法location里要带上两个proxy_http_version和Connection的header连接复用才会真正生效location / { proxy_http_version 1.1; proxy_set_header Connection ; proxy_pass http://backend_pool; }HTTP/1.1 默认是长连接如果Nginx与后端之间用的是HTTP/1.0每次请求后连接就会断开keepalive就白配了。我自己压测的时候对比过同样的请求量配上keepalive后延迟平均能降20%~30%对短小请求的提升尤其明显。3. 动静分离高性能Nginx网站的黄金落点3.1 为什么静态请求不该打到后端很多用Tomcat或者PHP的服务默认会把CSS、JS、图片这些静态资源也交给应用服务器处理。应用服务器每接一个静态请求都要走一遍完整的请求解析框架这其实很浪费因为静态文件的本质就是读文件、返回内容不需要任何业务逻辑。Nginx在处理静态文件上有着天然优势它不依赖Tomcat的线程池直接通过系统调用读取文件并返回。所以动静分离的核心思路就是让Nginx直接处理静态请求只把动态请求转发给后端。这样一来后端只需要关心真正的业务请求整体吞吐会明显提升。一个典型的配置是这样的location /static/ { alias /data/www/static/; expires 7d; access_log off; }expires 7d表示浏览器可以把这个路径下的资源缓存7天。对于图片、JS、CSS这类文件名一般会带版本号的资源缓存策略可以激进一点对于API返回的JSON绝不能加这种缓存。3.2 location 匹配优先级最容易背锅的配置细节理解location的匹配规则是Nginx配置的基础功也是面试常客。规则我之前梳理过多遍真正记到脑子里就这一句话精确匹配优先于前缀匹配前缀匹配分普通和正则带^~的优先级高于正则正则按书写顺序匹配。列一下具体规则location /path精确匹配写什么就必须是什么匹配后立即停止。location ^~ /path普通前缀匹配一旦匹配上就不再查正则表达式。location ~ /path或location ~* /path正则匹配~*不区分大小写按照文件中出现的顺序逐一尝试。location /path普通前缀匹配优先级最低按最长匹配被选中。实际排错中最典型的场景是定义了一个正则location ~ \.(gif|jpg|png)$来处理图片结果发现页面上的图片有些走了正则、有些走了前缀匹配于是出现某些图片被判为动态请求转发到后端。原因就是忘记了一个前缀匹配以^~开头优先级压过了正则。我的建议是静态资源统一用带^~的普通前缀匹配比如location ^~ /static/明确告诉Nginx命中这个前缀后直接走静态处理不要去碰正则。这个写法可以避免大量莫名其妙的坑。3.3 静态文件高性能三件套sendfile、tcp_nopush、gzip处理静态文件时有三个配置项对性能影响很大属于低垂果实级别的优化sendfile on; tcp_nopush on; gzip on;sendfile的作用是让Nginx直接在内核态把磁盘文件数据发到网络连接跳过用户态缓冲区复制。没有它文件数据要经过多次内核和用户态之间拷贝静态文件响应速度会有明显差距。tcp_nopush配合sendfile on使用它让Nginx在发送响应头和数据时尽量合并数据包减少小包数量。在传输大文件时效果明显但对小请求作用有限。gzip压缩对文本类资源HTML、CSS、JS、API返回的JSON效果拔群我一般还会配上gzip_types text/plain text/css application/json application/javascript image/svgxml; gzip_min_length 1k;注意图片文件JPEG、PNG本身已经是压缩格式再开gzip只会浪费CPU。gzip_min_length 1k是为了避免压缩那些过小、压缩后反而更大或收益极低的响应。这也是一个常见误区很多人一股脑把gzip_types写得很全实际上对图片毫无帮助。4. 事件驱动模型从一个连接一个进程到一个进程百万连接4.1 master/worker 的进程骨架Nginx高并发的底层支撑和它的进程模型直接相关。启动Nginx后你会看到一个master进程和若干个worker进程。master进程负责读取配置、管理worker生命周期、接收信号做平滑升级等worker进程才是真正处理请求的干活的。这种设计不是炫技而是实实在在的收益worker进程之间相互独立一个worker崩溃不会影响整体服务master可以快速拉起新的worker。多worker可以充分利用多核CPU每个worker进程绑定一个或几个CPU核心减少进程切换。master进程以普通用户权限运行worker才是真正以较低的权限处理请求安全性更好。配置文件中对应的是worker_processes auto; # 通常设为CPU核心数 worker_connections 1024;worker_processes auto会让Nginx自动探测CPU核数。我是建议显式设置为N核配合worker_cpu_affinity auto;让每个worker进程尽量固定在独立核心上能减少CPU缓存的竞争对高流量站点有帮助。4.2 异步非阻塞与 epoll为什么高并发不靠堆机器Nginx之所以能用很少的进程扛住大量并发连接核心在于异步非阻塞的事件驱动模型。同样讲一个连接传统的Apache为每个连接分配一个进程或线程连接数一上来线程Context切换的开销、内存占用都会爆炸。Nginx则是一个worker进程里同时管着成千上万个连接通过epoll这样的事件通知机制只处理那些真正发生了读写事件的连接。我比较喜欢用一个等座位的例子来解释Apache像一个餐厅里每个客人配一个专属服务员客人越多服务员越多最后走廊里全是人Nginx像值班经理一个人同时盯着整个大厅的客人谁举手了就去处理谁空闲时间也不会被占用。在Linux下Nginx默认使用epoll事件模型这是内核提供的高效IO多路复用机制。它让Nginx可以把大量连接的全部状态放在内核的事件表里每次只获取活跃事件处理完再继续等待不产生阻塞等待的空转。4.3 worker_connections 的计算与调优建议worker_connections代表每个worker进程可以同时打开的连接数上限。Nginx作为反向代理时一个客户端请求通常要消耗两个连接一个连接客户端一个连接上游。所以理论上单worker能支撑的最大并发请求数约等于worker_connections / 2。整体最大并发数的估算公式最大并发连接数 ≈ worker_processes × worker_connections 实际并发请求数 ≈ worker_processes × worker_connections / 2比如一台8核服务器worker_processes 8worker_connections 4096理论上最多能同时维持约32000个并发连接能承载的并发请求数大概16000。当然这只是粗略估算实际还要看每个请求占用的时间、带宽和内存大小。调优上我的建议是先看默认值不要一上来就把worker_connections调到65535连接数上限受系统文件描述符限制需要同步调高ulimit或nginx的worker_rlimit_nofileworker_rlimit_nofile 65535;这个配置的作用是提升worker进程可打开的文件数上限不配的话即使你把worker_connections调大了系统层面的限制还是会让Nginx在连接数升高时报too many open files。我踩过一次这个坑压测到两万人同时在线时页面开始间歇性打不开查了半天才定位到是文件描述符耗尽了。5. SSL证书、多站点与共享文件部署环节的常见需求5.1 给Nginx加SSL证书的正确姿势现在HTTP明文基本没法出门见人了搜索引擎、浏览器都会对纯HTTP站点做不安全提示。所以给Nginx配置SSL证书几乎成了上线前的必做项。第一步是准备好证书文件。从证书服务商下载时一般会给两个文件证书文件通常是xxx.crt或xxx.pem和私钥文件通常是xxx.key。有的还会有ca_bundle.crt证书链文件。把文件上传到服务器某个安全目录然后配置server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; }配置完成后一定记得执行nginx -t检查配置然后nginx -s reload加载。浏览器打开HTTPS地址点小锁看证书是否有效。这里最容易出问题的是证书链不完整如果只填了域名证书而缺少中级CA证书浏览器会报证书链不完整表现为部分电脑正常、部分电脑报错。建议直接用证书商提供的fullchain.crt域名证书CA证书链拼在一起文件省心很多。5.2 一个Nginx承载主域名与多个二级域名业务经常会用到主域名加多个二级域名。比如主站example.com管理后台admin.example.com静态资源域名static.example.com。Nginx处理多域名的方式是在配置里创建多个server块用server_name区分。server_name支持精确域名和通配符server { listen 80; server_name example.com www.example.com; # 主站配置 } server { listen 80; server_name admin.example.com; # 后台配置 } server { listen 80; server_name *.example.com; # 兜底子域名 }注意server_name的匹配顺序精确匹配优先于通配符起始匹配通配符起始优先于通配符结尾再往后才是正则。实际项目中我更喜欢把每个站点的配置拆成独立文件放到/etc/nginx/conf.d/目录下主配置里用include /etc/nginx/conf.d/*.conf;引入。这样站点多起来之后改一个站点的配置不影响其他站点排查问题也方便。5.3 root 与 alias共享文件的目录映射Nginx共享文件这个概念其实是用Nginx把服务器上某个目录直接暴露出来供局域网或公网下载最常见的就是文件服务。先看root和alias的区别因为它俩特别容易搞混。root会完整映射请求路径而alias是别名映射。比如location /download { root /data/files; }访问/download/a.zip实际读取的是/data/files/download/a.zip。如果/data/files下并没有download这个目录就会404。换成aliaslocation /download { alias /data/files; }访问/download/a.zip实际读取的是/data/files/a.zip路径中location匹配的那段前缀被alias直接替换掉了。做共享文件时我常用的完整配置是location /download { alias /data/files/; autoindex on; # 开启目录列表 autoindex_exact_size off; # 显示可读的文件大小 autoindex_localtime on; # 文件时间显示为本地时间 charset utf-8; }autoindex on会让访问目录时自动生成一个文件列表页面有点像逛FTP目录非常适用于共享文件下载场景。如果想让文件直接被下载而不是在浏览器里打开可以加一行add_header Content-Disposition attachment;5.4 图形化方案Nginx Proxy Manager如果你不习惯天天写配置文件还想管理多个站点和证书可以关注一下 Nginx Proxy Manager。它本质上是基于Nginx做了一层Web管理界面可以在浏览器里创建代理规则、申请SSL证书、配置访问权限。部署方式通常是一个Docker容器适合网络环境允许使用容器服务的场景。不过我不建议把核心业务完全依赖图形化工具毕竟Nginx配置的灵活性、可审计性命令行文件的方式更靠谱一些。图形化工具更适合内网小工具、个人项目、快速试验环境。6. 从安装到平滑升级系统运维视角的关键操作6.1 编译安装还是包管理器安装安装Nginx有两种主流方式用系统包管理器yum install nginx或apt install nginx或者源码编译安装。包管理器安装的优势是方便、依赖自动处理配置目录规范。但它也有短板比如CentOS的默认源里没有Nginx需要先扩展EPEL源或Nginx官方源同时版本更新慢想用新功能往往要等官方仓库。Debian系稍微好一点但仓库里的Nginx版本通常也不是最新。源码编译安装虽然步骤多一点但好处很明显可以定制编译参数大不了去掉不需要的模块减小体积。可以指定编译进openssl、http_realip_module、http_ssl_module等模块很多发行版默认不带。版本完全自己掌控平滑升级的时候特别方便。编译安装前需要准备依赖库pcre正则表达式、zlib压缩、opensslSSL。从PCRE官网下载源码时推荐pcre-8.45和Nginx兼容性较好这个版本的稳定性在社区里口碑不错。最小化编译安装流程大致是wget http://nginx.org/download/nginx-1.24.0.tar.gz tar xzf nginx-1.24.0.tar.gz cd nginx-1.24.0 ./configure --prefix/usr/local/nginx --with-http_ssl_module --with-http_realip_module make -j$(nproc) make install编译过程中如果报缺少库就yum install -y pcre-devel zlib-devel openssl-devel补上。这一步很常见缺啥装啥不用慌。6.2 麒麟系统 / aarch64 纯内网安装的注意事项国产化环境比如麒麟操作系统和aarch64架构的纯内网机器是现在不少公司要面对的场景简单说下我的经验。麒麟系统基于Linux生态包管理器通常是yum或dnf如果机器能访问公网流程和CentOS基本一样。但纯内网环境没有外网最常见方案是提前在联网机器上把RPM包或源码包下好拷贝到内网机器上安装。如果把系统依赖包一股脑都拷进去容易遇到libpcre.so.1这种依赖缺失比较麻烦。我的建议是纯内网环境尽量用源码编译因为依赖可控。提前准备好gcc、make的安装包以及pcre、zlib、openssl三件套的源码包内网机器上依次编译安装最后再编译Nginx。aarch64架构下只要这三个依赖库能编过Nginx本身基本没有架构障碍。镜像源下载慢的问题也提一句国内有很多Nginx二进制包的镜像源用对地方速度能提升不少。下载RPM包时注意选择对应的系统版本和架构aarch64的包在x86_64上装不了反之亦然。6.3 平滑升级原理与操作步骤Nginx平滑升级是我工作中特别喜欢演示的一个功能。它允许升级Nginx版本不需要停服、不打断正在进行的请求。主要依赖master进程的信号机制。原理是这样的旧master进程收到USR2信号后会启动一个新的master进程新master会加载新的Nginx二进制文件新版本两个master进程同时运行。然后旧master收到WINCH信号会优雅地关闭旧的worker进程不再接收新连接等正在处理的请求完成后自然退出。最终旧master收到QUIT信号彻底退出。具体操作步骤以从旧版本升级到新版本为例# 1. 准备新版本下载并解压到任意目录 tar xzf nginx-1.26.0.tar.gz cd nginx-1.26.0 # 2. 同样带上原来的编译参数重新编译不要make install ./configure --prefix/usr/local/nginx --with-http_ssl_module make # 3. 先找出旧nginx的master进程PID cat /usr/local/nginx/logs/nginx.pid # 4. 备份旧二进制 cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old # 5. 用新编译好的二进制替换旧二进制 cp objs/nginx /usr/local/nginx/sbin/nginx # 6. 通知旧master启动新master两边并存 kill -USR2 $(cat /usr/local/nginx/logs/nginx.pid) # 7. 此时旧master PID会记录在 logs/nginx.pid.oldbin 中 kill -WINCH $(cat /usr/local/nginx/logs/nginx.pid.oldbin)等待几秒确认新worker已经接替旧worker处理请求并且系统没有异常日志后最后再关掉旧masterkill -QUIT $(cat /usr/local/nginx/logs/nginx.pid.oldbin)平滑升级最怕两点一是新版本编译参数漏了原先的模块升级后功能缺失二是替换二进制后没有立刻做nginx -t语法错误导致新master起不来。我每次操作前都会写一个checklist把./configure的原始参数记录下来升级时逐项对照。6.4 Windows下Nginx的坑与进程管理Windows环境跑Nginx的情况也不少尤其是本地开发调试。但Windows下的Nginx和Linux下的表现差异比较大有几个点提前知道能少走弯路。Windows版Nginx的目录不要放在带空格的路径下如C:\Program Files\nginx经典的坑是启动时报[error] CreateFile()这类路径相关错误。尽量放到D:\nginx这类纯净路径。Windows下无法用kill信号控制master只能通过命令或任务管理器nginx.exe -s stop # 强制停止 nginx.exe -s quit # 优雅停止 nginx.exe -s reload # 重载配置nginx -s reload在配置有语法错误时会直接报错并拒绝重载所以修改配置后务必先执行nginx -t。Windows下sbin目录就是Nginx安装目录本身配置目录结构也是一样的。另外Windows版的Nginx不支持旧版本平滑升级跨主版本也没有Linux里那么完善的信号体系。本地调试够用但真要上生产还是优先Linux环境这个差异不是我说的是官方文档里就有的限制。7. 排错实战三个高频报错背后的排查链路7.1 nginx: [emerg] createfile() 报错——Windows路径与权限第一个报错是Windows环境下相当有代表性的nginx: [emerg] createfile() d:/phpstudy_pro/www/admin2.com/nginx.htaccess failed (2: The system cannot find the specified file)这个报错我帮人看过几次很多人第一反应是Nginx怎么会生成nginx.htaccess文件其实问题出在配置里某个日志或临时文件路径指向了这个位置而Nginx启动时发现目录或者文件不存在尝试创建却失败。排查链路大致是这样先看nginx.conf里error_log和各个server块里的access_log路径。路径中的目录如果不存在Windows下Nginx不会自动创建会报createfile()失败。检查路径里的盘符大小写、正反斜杠。Windows下Nginx对d:/和D:/都能识别但路径所在的上级目录必须真实存在。检查目录是否有写入权限。用Windows服务方式跑Nginx的时候服务账户可能是SYSTEM也可能是一个受限账号对D:\phpstudy_pro或www目录没有写权限就会报这个错。检查是不是真的把access_log指到了一个nginx.htaccess文件上。这是一个伪装成Apache.htaccess文件名的日志文件设置很多人从Apache迁移过来习惯性把虚拟主机配置里的日志路径写成了奇怪的名字。解决办法很直接把日志路径改成规范路径比如access_log d:/phpstudy_pro/www/admin2.com/logs/access.log;然后手动把logs目录建好。目录权限要给到运行Nginx的账户。改完再nginx -t测语法能顺利通过基本就解决了。7.2 no required ssl certificate was sent——证书配置不完整第二个报错也很典型出现在HTTPS握手阶段no required ssl certificate was sent这个报错的意思是客户端没有发送服务器要求的SSL客户端证书。也就是说Nginx配置了ssl_verify_client on开启了双向TLS认证要求客户端也出示证书但客户端浏览器或业务方没有配置客户端证书于是握手直接失败。遇到这个报错先想清楚一个问题你的业务真的需要双向认证吗大部分常规网站是不需要的。如果只是想让用户通过HTTPS安全访问压根不应该开启ssl_verify_client在Nginx配置里找到并改成ssl_verify_client off;如果是接口层面确实需要双向认证那就要把合适的客户端证书安装到调用方的设备或程序里确保TLS握手时能给出服务端信任的证书。这里的证书链也要完整服务端配置了ssl_client_certificate客户端出示的证书必须由该CA签发。这个报错和普通证书配置不全报的 certificate has expired 或 unable to get local issuer certificate 不一样后两个往往是服务端证书本身的问题前者则是验证客户端证书环节出的问题。定位思路完全不同不要混在一起排查。7.3 用VSCode格式化与校验nginx配置最后一个不算报错但能省很多不必要的排查时间。Nginx配置写多了缩进混乱、层级嵌套看不清我非常建议用VSCode配合Nginx格式化插件。VSCode扩展市场搜索nginx-formatter或者nginx config formatter安装后在nginx.conf文件上执行格式化命令即可。不过格式化插件是基于规则重排文本的对于个别手写的不规范配置格式化后可能改变语义所以格式化完一定要跑一遍nginx -tnginx -t会打印配置文件语法检查结果如果配置有问题会明确提示第几行有误。我每次改完配置都执行这条命令这个习惯帮我挡住了大量失误无论是location少了一个括号还是server_name后少了分号都能在reload前发现。配合系统层面还可以在每次reload前自动跑一轮nginx -t nginx -s reload写成一行命令效率很高。最后再分享一点个人经验。Nginx上手容易精通难难在那些边界情况——proxy_pass的斜杠、root和alias的路径映射、正则与前缀匹配的优先级、被动健康检查的时机。我见过很多线上事故最终查根因都不是复杂逻辑而是这些基础细节的疏忽。我自己的习惯是每个项目上线前都做一次配置走查把nginx -t结果、证书有效期、进程状态、错误日志扫一遍每次改动配置都备份一份带日期的副本。时间久了你会发现这套笨功夫其实是最有效的护身符。Nginx还有很多高级玩法比如限流、缓存、动态模块这篇没展开后面有机会再单独聊。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进