
简介面向Linux运维与Nginx学习者这份资料包系统梳理了从基础指令到集群架构的完整知识体系适合初学者进阶以及运维工程师随用随查。内容按基础篇、进阶篇、架构篇、模块篇划分每个阶段侧重点不同涵盖nginx指令配置、服务集成、负载均衡、反向代理及Lua扩展等核心主题并结合多个动手案例与启动流程、HTTP配置块解析图例辅助理解。资源共17个文件压缩包大小19.88MB以PDF和MD笔记为主搭配war包、js样例、xmind思维导图及png图解其中MD/PDF双格式学习笔记按主题分册详细记录了每个指令的作用、参数、作用域、注释、使用场景与示例既可作为系统教程也可作为工作手册。已有184人浏览学习适合正在从事或准备从事Linux服务管理的开发者快速构建Nginx实战能力。1. 先看穿这个标题Nginx 学习资料里真正值钱的是「服务」二字Nginx 是 Linux 服务器上最常被提起的 Web 服务软件高并发、反向代理、负载均衡是它身上最显眼的三张标签。很多人拿到一份资料就开始抄配置看到server { listen 80; }就跑访问出 Welcome 页就以为会了。但这个标题真正该让你停下思考的是「服务」这两个字安装、启动、配置、排障、保活每一个环节背后都是 Linux 系统工程思维在托底。它能帮你解决的是单机静态站、前后端分离联调、多后端负载均衡、给内网多个服务做统一入口这类生产问题适合刚接手服务器、要在公司内网搭服务的开发者也适合准备把个人项目部署到云主机的前端同学。一个反直觉的结论是Nginx 跑得稳不稳和你会不会写花哨配置关系不大关键在两条——理不理解 master/worker 的进程模型知不知道请求进来之后会经过哪几个环节再回来。把这两条串起来看后面所有配置都只是填参数。2. 先让 Nginx 装起来并跑通第一个站点安装验证与配置骨架2.1 从 Linux 服务视角学 Nginx 先要分清在装什么先泼一盆冷水Nginx 不是装完就完事的普通软件它是一个由 master 进程、worker 进程组成的服务体系。master 负责读配置、管理工作进程、接收 reload 信号worker 才真正去 accept 连接、处理请求。每多一个 worker就多一套独立的 epoll 事件循环。单 worker 在普通云主机上扛住上千并发是常态多 worker 是为了吃满 CPU 多核而不是「进程越多越快」。常见做法是用发行版包管理器安装而不是一上来就源码编译。多数业务场景用系统包足够优点是开机自启、升级方便、依赖干净缺点是默认模块组合未必合你胃口。我一般先apt或dnf装一个稳定版再通过nginx -V看编译参数确认需要的模块比如http_ssl_module、http_v2_module都在不在。缺模块时再考虑从源码加模块重编但那是少数情况。这一层还要留意版本认知当前主流发行版自带的 Nginx 大版本多数在 1.24 到 1.26 之间语法和核心行为差异不大。只是别把旧资料里过时的习惯带进来比如 HTTP/2 需要单独确认模块支持、upstream keepalive的配置位置在新版本里有更明确的语义。配置语法这些年变化很小把一套骨架学会换到哪个发行版都能很快上手。2.2 用包管理器安装并用最少命令验证服务状态以 Debian/Ubuntu 系为例一条链路装完并确认状态sudo apt update sudo apt install -y nginx systemctl enable --now nginx systemctl status nginx --no-pager nginx -v ss -lntp | grep :80前两条完成安装enable --now同时完成开机自启和立即启动避免重启后服务没拉起来systemctl status看主进程状态nginx -v看版本号ss -lntp | grep :80确认 80 端口确实有进程在监听这一步能避免出现「服务起来了但根本没在听」的错觉。CentOS/RHEL 系把apt换成dnf或yum即可其余命令通用。ss输出里看到nginx: master监听在*:80说明安装成功。此时访问http://服务器IP能看到默认欢迎页。这一步很多人卡在云主机安全组没放行 80 端口——服务器内部正常和外部网络可达是两件事。排查时先curl 127.0.0.1通了再去查防火墙和安全组。日常维护命令值得形成肌肉记忆nginx -t检查语法nginx -s reload平滑重载nginx -s stop强制停systemctl restart nginx是完整重启。区别在于 reload 不断存量连接restart 全断。所以变更配置时首选nginx -t nginx -s reload不要轻易 restart。还要提一下配置文件的组织习惯。主配置/etc/nginx/nginx.conf里通常用include把conf.d/*.conf或sites-enabled/下的站点配置加载进来。我自己的习惯是把每个站点拆成一个独立文件放在/etc/nginx/sites-available/再软链到sites-enabled/。删除站点时只删软链保留原文件这也算给自己留后悔药。2.3 写第一个可用站点配置从 server 块到 location 的请求路径最小可用配置不复杂但每一行都要知道为什么。以下是我在模拟项目X里常用的骨架user www-data; worker_processes auto; error_log /var/log/nginx/error.log warn; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; server { listen 80; server_name test.example.com; root /var/www/demo; index index.html; location / { try_files $uri $uri/ 404; } } }user决定 worker 以哪个系统用户去读静态文件权限不够会直接返回 403worker_processes auto让 Nginx 按 CPU 核数自动拉起对应数量的 workerworker_connections是每个 worker 能同时保持的最大连接数默认 1024 在低并发验证阶段够用。server_name决定用哪个 Host 头匹配这个站点root是站点根目录try_files按$uri、目录、最终 404 的顺序查找资源这是现代前端路由场景最常见的入口写法。参数边界要清楚worker_processes不要超过物理核数调多反而因为上下文切换变慢worker_connections调高到 4096 很容易但要同步看系统ulimit -n和内核net.core.somaxconn否则配置只是心理安慰。改完配置必须跑nginx -t通过后nginx -s reload。验证时不要只看页面能不能开要看请求是否确实落进预期配置curl -I http://127.0.0.1/ curl -H Host: test.example.com -I http://127.0.0.1/第一条不带 Host走默认 server第二条显式带 Host验证server_name匹配是否正确。如果两条结果不同大概率是server_name没写对或默认 server 被其他配置抢占了。这个姿势就是后面所有反向代理调试的基本功先确认请求落在哪个 server、哪个 location再去看转发规则。到这一步「装好并跑通」已经完成。下一章进入生产价值的核心把静态之外的流量正确转给后端以及让多台后端分担压力。3. 生产最常用的三种配置反向代理、负载均衡与静态缓存3.1 反向代理proxy_pass 是前后端分离联调的核心反向代理就是让 Nginx 在 80/443 端口接客再把请求转发给内网后端。后端也许是某个端口上的 Java、Node、Go 服务也许是 Unix Socket。对前端开发者来说最直观的价值是同源策略消失页面在 80 端口接口也在 80 路径下浏览器不再出现跨域报错。先看一段最标准的配置server { listen 80; server_name test.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; } location / { root /var/www/frontend; index index.html; try_files $uri $uri/ /index.html; } }location /api/把以/api开头的请求转到本机 8080 端口proxy_pass http://127.0.0.1:8080;后面没有路径表示保持原始 URI 透传。proxy_set_header三行属于基本功Host保持域名X-Real-IP让后端拿到真实客户端 IPX-Forwarded-For给后端记录完整代理链。少了Host很多按域名做路由的后端会返回 400 或直接拒绝。这一节有个经典坑proxy_pass末尾带不带/行为完全不同。http://127.0.0.1:8080;是原始 URI 原样转发http://127.0.0.1:8080/;则会把 location 前缀去掉再拼接。也就是说请求/api/orders前者到后端仍是/api/orders后者变成/orders。后端接口路径按哪种方式设计就用哪种方式转发。前后端约定不一致时第一晚联调就翻车这是我在某跨平台系统里实际踩过的坑。如果后端接口还要走 WebSocket需要在 location 里加两行升级头location /ws/ { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; }Upgrade和Connection upgrade是 WebSocket 握手的关键proxy_read_timeout要调到足够长否则长连接会被 Nginx 默认 60 秒掐断。这是反向代理里最容易忽略的隐藏参数。3.2 负载均衡upstream 的轮询、权重与节点摘除后端不止一台时用upstream定义一组后端再在proxy_pass里指向这个组upstream backend_api { server 10.0.0.11:8080 weight3 max_fails2 fail_timeout30s; server 10.0.0.12:8080 weight1 max_fails2 fail_timeout30s; server 10.0.0.13:8080 backup; keepalive 32; } server { listen 80; server_name test.example.com; location /api/ { proxy_pass http://backend_api; proxy_set_header Host $host; proxy_http_version 1.1; proxy_set_header Connection ; } }参数含义weight控制流量比例3:1 就是把三倍请求分到 10.0.0.11max_fails与fail_timeout组成最朴素的被动健康检查——30 秒内失败 2 次就临时摘除该节点 30 秒backup表示备用节点平时不参与其余节点不可用才顶上keepalive 32是每个 worker 与后端之间保留的空闲长连接数量能明显降低 TCP 握手开销。proxy_http_version 1.1和Connection 必须配合出现否则长连接根本建不起来等于keepalive白写。默认负载均衡算法是轮询。需要会话保持时换成ip_hash同一来源 IP 固定打到同一后端需要按后端压力动态分配时用least_conn。选型没有绝对好坏ip_hash在客户端都走固定出口 IP 时容易打偏least_conn依赖后端能反映真实负载否则效果和轮询差不多。节点摘除的常见做法是注释掉对应server行但每次都 reload 不够优雅。我一般临时把权重改成 0让流量自然排空后再处理后端避免 reload 瞬间中断存量长连接。任何变更都放低峰期做这是服务稳定性的一条底线。3.3 静态资源缓存expires、Cache-Control 与命中率观察静态资源是 Nginx 性价比最高的处理对象图片、CSS、JS 交给它直接响应动态请求回源后端前后端各干各的。常见配置location ~* \.(png|jpg|jpeg|gif|css|js|woff2)$ { root /var/www/frontend; expires 30d; add_header Cache-Control public, max-age2592000; access_log off; }expires 30d让 Nginx 自动在响应头里带Expires和Cache-Control浏览器在 30 天内重新访问直接使用本地缓存add_header是显式加固一遍Cache-Controlaccess_log off给静态文件关掉访问日志减少磁盘 IO。这里有个反直觉的坑部署新版本时如果前端文件名不带 hash 后缀客户端会继续用旧缓存改了半天界面还是老样子。所以静态资源要么文件名带内容 hash要么发布时主动更新版本路径。验证缓存是否生效不用上监控平台。第一次访问响应 200响应头里有Cache-Control: max-age2592000第二次刷新在浏览器开发者工具里看到from disk cache说明浏览器缓存已经接管。服务器端想确认请求没有打到后端可以开启stub_status模块在配置里加一个/nginx_status的 location然后看Active connections、accepts、handled、requests这几个核心计数器。如果accepts和handled长期不一致说明连接层有堆积需要回头查 worker 连接数和内核队列长度。到这里反向代理、负载均衡、静态缓存三大场景都落地了。可以再补一个细节开启 gzip 能显著压缩文本类响应体积代价是 CPU 开销。我一般只在文本类 MIME 上开启gzip on; gzip_comp_level 5; gzip_min_length 1k; gzip_types text/plain text/css application/json application/javascript;gzip_comp_level 5是压缩率和 CPU 消耗的平衡点再往上压收益递减明显gzip_min_length 1k避免对过小的响应做无谓压缩因为压缩头本身就有开销。4. Nginx 翻车排查五个高频报错与对应的解决路径4.1 502 Bad Gateway后端明明在Nginx 却拿不到响应现象页面 502error.log里出现connect() failed (111: Connection refused) while connecting to upstream或者upstream prematurely closed connection while reading response header from upstream。原因分两类。如果错误是 Connection refused多半是后端进程没启动或监听端口和proxy_pass不一致如果是prematurely closed connection则可能是后端处理超时、响应体过大被中间层掐断或后端自己崩了。解决顺序先ss -lntp | grep 8080确认后端端口在听不在就直接去查后端服务日志。再确认 Nginx 配置文件里的地址端口与后端实际监听一致很多 502 只是这里写错一个数字。还解不掉时临时加proxy_buffering off;关掉缓冲试一次。关掉立刻恢复说明是缓冲空间不够再按需调proxy_buffer_size和proxy_buffers。如果是慢接口超时调大proxy_read_timeout默认 60 秒对耗时操作确实太短。这类问题的排查路径应该是后端进程 → 端口 → Nginx 转发地址 → 超时与缓冲 → 后端自身日志。绝大多数 502 停在前两层别一上来就动超时参数。4.2 403 Forbidden目录权限和启动用户之间的隐形矛盾现象静态页面能开但某个目录下的资源全部 403。错误日志里出现Permission denied或open() /var/www/... failed (13: Permission denied)。原因Nginx 默认user www-data;但业务把文件放在 root 用户目录下worker 进程没有读权限。另一个常见来源是开启了 SELinux 的发行版Nginx 进程域与文件标签不匹配权限看似对实际被内核拦下。解决先确认文件权限是 644/755而不是 600/700。然后别只看最后一级目录用namei -l /var/www/demo/index.html检查路径上每一层目录的权限因为 Nginx 访问文件要求每一层都有x权限。SELinux 环境里可以先临时执行setsebool -P httpd_read_user_content 1验证是否和 SELinux 有关长期方案是给目录打正确标签chcon -Rt httpd_sys_content_t /var/www。顺带提醒排查 403 时不要因为省事就把整个目录 chmod 777。这确实能立刻解决但也等于把安全边界拆了。正确姿势是让站点文件归属到 Nginx 的运行用户组目录 750、文件 640 就足够。4.3 配置改了不生效nginx -t 通过行为却还是旧的现象改了配置文件nginx -t提示 syntax okreload 也执行了但行为没有变化响应头或转发目标还是旧配置。原因最常见的是改错了文件。Nginx 主配置通过include把conf.d/*.conf和sites-enabled/下的软链都加载进来实际生效的可能是另一个文件里的同名server块。还有一种情况是nginx -s reload时 Shell 的 PATH 指向了另一个二进制和 systemd 里跑的不是同一个。解决用nginx -T把最终生效的完整配置一次性打出来搜索server_name和listen你改的内容在不在里面一目了然。再执行which nginx和systemctl show nginx -p ExecStart对比二进制路径。改完以后curl -I看响应头里的 Server 字段判断是否还是旧进程。养成nginx -t nginx -s reload连着跑的习惯这一条能省掉大量玄学排查时间。4.4 listen 端口冲突address already in use 的另一层原因现象启动或 reload 时报bind() to 0.0.0.0:80 failed (98: Address already in use)。原因80/443 被占用。可能是另一个 Web 服务先启动了也可能上一次 Nginx 没有正确退出还有一种是配置里写了两个相同的listen 80 default_server;语法检查不报启动时才暴露。解决先ss -lntp | grep :80看占用进程的 PID再用ps -p PID -o comm确认身份。如果是旧 Nginx用kill -QUIT 主进程PID优雅退出再重新启动如果是其他服务要么停掉要么让 Nginx 换端口。同一份配置里重复listen的情况搜索配置文件里的listen行就能发现。不要一冲突就kill -9先给进程几秒时间处理完存量请求。4.5 日志时间不对排查问题先看 error.log 的时区与级别现象出问题后翻日志发现时间全是昨天或者日志里根本没有报错但服务确实在异常。原因Nginx 默认错误日志走系统本地时间和 warn 级别。服务器时区如果是 UTC你按北京时间对表自然对不上日志级别默认是 warninfo和debug级别的信息被过滤了很多线索根本没被记录。解决主配置里显式写error_log /var/log/nginx/error.log warn;确认级别符合预期。需要深挖时临时降到debug复现问题后立刻改回warn否则 debug 日志会以极快速度写满磁盘。服务器时区用timedatectl set-timezone Asia/Shanghai按实际需要统一或者直接接受 UTC 并在查日志时做时间换算。访问日志和错误日志要分开看。定位慢请求用 access log 里的$request_time字段定位失败原因用 error log。两个文件配合才能把「什么时候慢」「为什么慢」拼成完整链条。下面这张定位表可以作为快速参考报错关键词大概率原因第一步动作Connection refused后端没起或端口不对ss -lntp确认端口Permission denied文件权限或 SELinuxnamei -l查路径Address already in use端口被占或配置重复ss -lntp找 PIDupstream timed out后端处理超过超时时间调proxy_read_timeoutno resolver defined转发域名无法解析配置resolver5. 从能跑到跑稳压测验证与四个值得先调的稳定性参数调优最忌讳改完参数直接上生产。我习惯先在测试环境用ab打一轮基线。ab来自 apache2-utils 包Debian 系安装命令是sudo apt install -y apache2-utils。压测命令本身不复杂ab -n 10000 -c 200 http://127.0.0.1/test.html-n是总请求数-c是并发数。结果里只看三个指标Requests per second、Time per request、Failed requests。基线数据留档改完参数再打一轮对比才有说服力。没留基线的调优都是心理安慰。四个值得先调的参数按优先级排序第一是连接数。worker_connections 4096;配合worker_processes auto;让单机并发能力先上去。但必须同步确认ulimit -n大于worker_processes × worker_connections否则内核直接卡在 accept 队列。第二是超时。proxy_connect_timeout 5s; proxy_read_timeout 60s;给上游设一个合理闭环避免一个慢接口拖住整个 worker。第三是长连接。keepalive_timeout 65;和keepalive_requests 100;控制客户端到 Nginx 的保持时间过短增加握手过长占用连接。第四是压缩。gzip on; gzip_comp_level 5;对文本类响应收益明显但压测时多留意 CPU 是否被打满。这些参数不是越大越好。超时调太大坏节点迟迟不被发现用户直接卡死连接数调太大内存占用上升云主机小规格时反而先把自己压垮。每改一个参数跑一轮压测对比基线两轮峰值稳定后才可以固化配置。跑稳之后再补一个最小闭环日志按天切割、配置变更自动检查、上游失败快速转移到备用节点。日志切割用 logrotate 的 daily 规则配合nginx -s reopen重开日志文件变更发布脚本里强制跑nginx -t不过检不允许 reload上游节点挂了时backup节点自动接盘。这套组合看似简单但能把「半夜被叫起来处理 502」变成「第二天看日志有一条告警记录」。我自己在模拟项目X里经历过一次印象深刻的事故压测数据很好看上线第二天下午高峰突然大面积超时。最后发现是worker_connections调大了但系统ulimit -n没改所有连接被内核卡死在 accept 队列里。从那以后我的习惯变成任何调参都同时查系统资源限制压测数据留档改动至少观察两轮峰值再固化。希望你调参时也能少走这条弯路希望帮到你。本文还有配套的精品资源点击获取