1. 为什么我建议你从源码编译而不是直接apt install很多人第一次接触nginx上来就是一句sudo apt install nginx装完跑起来看到欢迎页就觉得完事了。我早期也这么干直到线上环境需要用到http_v2模块、需要自定义日志格式、需要把第三方模块编进去的时候才发现包管理器装的那个版本根本不够用。所以这篇东西我打算从“为什么”讲起把安装和配置这两件事彻底说透。nginx本质上是一个事件驱动的高性能HTTP服务器和反向代理服务器。它的核心价值在于用极少的资源扛住极高的并发这跟传统的多进程/多线程模型有本质区别。你如果只是拿它当个静态文件服务器那确实随便装装就行但只要你涉及到反向代理、负载均衡、HTTPS卸载、视频流分发这些场景配置文件的每一个指令都值得你花时间搞清楚。这篇文章适合谁看如果你是刚接触Linux服务端的新手想从零把nginx跑起来并且理解每一步在干什么那正好。如果你已经用过nginx但配置全靠复制粘贴遇到404或者并发上不去就抓瞎那这篇也能帮你把知识体系补完整。我会从安装方式的选择讲起然后逐层拆解配置文件的结构最后把location匹配、反向代理、并发调优这些高频痛点单独拎出来讲。2. 安装方式的选择与实操2.1 包管理器安装与源码编译的取舍包管理器安装最大的好处是省事依赖自动解决升级也方便。但它的问题也很明显编译参数是发行版维护者定的很多常用模块默认没开版本也偏旧。比如你想用--with-http_v3_module或者某个第三方缓存模块包管理器版本基本没戏。源码编译的好处是你可以精确控制每一个编译选项想开什么模块就开什么想关什么就关什么二进制文件也干净。代价就是需要手动处理依赖升级的时候要重新编译。我的建议是开发环境或者学习用途包管理器装一个先跑起来没问题生产环境或者需要特定模块的场景老老实实源码编译。下面这张表可以帮你快速决策对比维度包管理器安装源码编译安装安装速度快一条命令慢需编译模块控制受限只能用预编译模块完全自定义版本更新跟随发行版仓库自己控制依赖管理自动解决手动安装适合场景快速验证、学习生产环境、定制需求2.2 源码编译的完整步骤与参数解读先装依赖。以常见的Linux发行版为例编译nginx需要gcc、make、pcre库用于正则、zlib库用于gzip、openssl库用于HTTPS。这些缺一个都会导致编译失败或者功能缺失。# 安装编译依赖 sudo apt update sudo apt install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev然后下载源码包。nginx的官方源码包命名规则是nginx-版本号.tar.gz建议选稳定版偶数版本号。下载完解压进入目录。wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf nginx-1.24.0.tar.gz cd nginx-1.24.0接下来是关键的编译配置环节。./configure这一步决定了最终二进制文件包含哪些功能。我列几个最常用的参数并解释为什么需要它们./configure \ --prefix/usr/local/nginx \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-stream \ --with-stream_ssl_module \ --with-threads--prefix指定安装路径我习惯放在/usr/local/nginx这样所有文件集中在一个目录下卸载的时候直接删目录就行不会污染系统。--with-http_ssl_module是HTTPS支持现在没有这个基本没法用。--with-http_v2_module开启HTTP/2对性能提升明显。--with-http_realip_module在反向代理场景下用来获取客户端真实IP这个后面会详细讲。--with-http_stub_status_module提供基础的状态监控接口。--with-stream是四层代理模块做TCP/UDP转发的时候需要。配置完成后执行make sudo make install。编译时间取决于机器性能一般几分钟内能完成。装完之后用/usr/local/nginx/sbin/nginx -V可以看到完整的编译参数确认你需要的模块都编进去了。注意每次升级nginx版本或者增删模块都需要重新执行./configure和make不能只替换二进制文件。另外编译参数一旦确定后续升级要保持一致否则可能出现模块不兼容。2.3 安装后的目录结构与启动验证源码编译安装后/usr/local/nginx下会出现几个关键目录。conf存放配置文件html是默认的静态文件目录logs放访问日志和错误日志sbin是二进制文件。这个结构很清晰跟包管理器安装的分散式布局完全不同。启动nginx直接执行sudo /usr/local/nginx/sbin/nginx。如果没有任何输出说明启动成功。然后用curl -I http://127.0.0.1验证应该返回200状态码。如果启动失败去logs/error.log看具体报错常见的是端口被占用或者配置文件语法错误。停止nginx有两种方式nginx -s stop是强制停止nginx -s quit是优雅停止处理完当前请求再退出。重载配置用nginx -s reload这个命令在实际运维中用的频率最高后面会反复提到。3. 配置文件结构逐层拆解3.1 从全局块到server块的层级关系nginx配置文件的核心结构是块状嵌套。最外层是全局块然后依次是events块、http块http块里面可以嵌套多个server块server块里面再嵌套location块。这种层级关系决定了指令的生效范围理解这一点是写好配置的前提。全局块主要设置影响整个nginx进程的参数比如运行用户、工作进程数、错误日志路径、PID文件位置。events块控制连接处理相关的参数最核心的是worker_connections它决定了每个工作进程能同时处理多少个连接。http块是配置最密集的地方大部分跟HTTP协议相关的指令都在这里。我见过很多人配置文件写了几百行但问他某个指令为什么放在这个位置答不上来。其实只要记住一个原则指令放在哪个块就对这个块及其子块生效。比如你在http块里写了gzip on那所有server都启用gzip如果你只在某个server里写那就只有那个server生效。3.2 全局块与events块的关键参数全局块里我重点说两个参数。worker_processes设置工作进程数一般设为CPU核心数或者auto。设多了进程间切换开销大设少了浪费CPU。worker_rlimit_nofile设置每个进程能打开的最大文件描述符数这个值要大于worker_connections乘以2因为每个连接需要两个文件描述符一个用于客户端一个用于后端。events块里worker_connections是最容易踩坑的参数。它跟worker_processes共同决定了理论最大并发连接数worker_processes * worker_connections。但注意这只是理论值实际能扛多少还受限于系统级的文件描述符限制和内存。我一般会把worker_connections设成10240或者更高然后同步调整系统的ulimit -n。还有一个multi_accept参数设为on时工作进程会一次性接受所有新连接在高并发场景下能减少系统调用次数。use epoll在Linux上基本是默认的不用手动指定但如果你在BSD系统上跑就要改成kqueue。3.3 http块中的MIME类型与日志格式定义http块里include mime.types这行很关键它引入了文件扩展名到Content-Type的映射表。没有这个浏览器下载CSS文件的时候可能当成纯文本处理页面样式就全乱了。default_type设置默认的MIME类型一般设为application/octet-stream。日志格式用log_format指令定义。默认的combined格式包含客户端IP、时间、请求行、状态码、响应大小、Referer、User-Agent。我建议自定义一个包含$request_time和$upstream_response_time的格式这两个变量在排查性能问题时非常有用。$request_time是nginx处理请求的总耗时$upstream_response_time是后端返回的耗时两者一对比就能知道瓶颈在前端还是后端。log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for rt$request_time urt$upstream_response_time;sendfile on开启零拷贝传输静态文件服务性能提升明显。tcp_nopush on和tcp_nodelay on配合使用前者在发送响应头时尽量填满数据包后者在长连接场景下禁用Nagle算法减少延迟。这两个参数看起来矛盾实际上nginx会根据场景自动切换。4. location匹配机制与反向代理实战4.1 location的匹配优先级与工作流location是nginx配置里最灵活也最容易出错的部分。它的匹配规则有一套明确的优先级精确匹配最高然后是前缀匹配^~然后是正则匹配~和~*最后是普通前缀匹配。同一优先级内正则匹配按配置文件中的顺序前缀匹配取最长匹配。很多人搞不清楚^~和~的区别。^~是前缀匹配一旦匹配上就不再尝试正则~是正则匹配会按照顺序逐个尝试。举个例子如果你有location ^~ /static/和location ~ \.css$请求/static/style.css会命中前者因为^~匹配后直接停止后续正则检查。实际工作流是这样的nginx先找精确匹配找到就直接用没找到就找最长前缀匹配如果这个前缀带有^~直接用否则记住这个最长前缀继续按顺序检查正则第一个匹配的正则生效如果正则都没匹配上就用之前记住的最长前缀。提示调试location匹配的时候可以在配置里临时加return 200 matched: $uri;来确认请求到底命中了哪个location比看日志快得多。4.2 反向代理的核心配置与真实IP传递反向代理是nginx最常用的功能之一。核心指令是proxy_pass它把请求转发到后端服务器。配置的时候有几个关键点proxy_set_header用来传递客户端信息proxy_connect_timeout和proxy_read_timeout控制超时proxy_buffering控制是否缓冲后端响应。真实IP传递是个高频问题。当nginx作为反向代理时后端服务器看到的客户端IP是nginx的IP不是真实客户端IP。解决办法是在nginx配置里设置proxy_set_header X-Real-IP $remote_addr和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for然后后端应用从这两个头里取真实IP。注意$proxy_add_x_forwarded_for会把nginx的IP追加到已有的X-Forwarded-For后面形成一条IP链。location /api/ { proxy_pass http://backend_server; 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_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 5s; proxy_read_timeout 30s; proxy_send_timeout 30s; }proxy_pass末尾带不带斜杠行为完全不同。proxy_pass http://backend/会把location匹配的部分替换掉而proxy_pass http://backend会保留完整路径。这个细节在配置API网关的时候特别容易搞错我踩过好几次坑。4.3 负载均衡策略与健康检查upstream块定义后端服务器池支持多种负载均衡策略。默认是轮询weight参数可以设置权重ip_hash根据客户端IP做会话保持least_conn把请求发给连接数最少的服务器。fair和url_hash是第三方模块提供的策略需要额外编译。健康检查分被动和主动两种。被动检查是nginx根据后端返回的状态码自动标记故障节点max_fails和fail_timeout控制这个行为。主动检查需要nginx_upstream_check_module这个第三方模块能定期发送探测请求更可靠但需要额外配置。upstream backend { least_conn; server 192.168.1.10:8080 weight3 max_fails2 fail_timeout10s; server 192.168.1.11:8080 weight1 max_fails2 fail_timeout10s; server 192.168.1.12:8080 backup; keepalive 32; }keepalive参数设置到后端的长连接池大小能显著减少TCP握手开销。但要注意开启keepalive后需要配合proxy_http_version 1.1和proxy_set_header Connection 才能生效否则nginx还是会用短连接。5. 高频问题排查与性能调优5.1 404错误的五种常见原因与定位方法404 Not Found是nginx最常见的报错但原因可能完全不同。第一种是文件真的不存在检查root或alias指向的目录下有没有对应文件。第二种是权限问题nginx运行用户没有读取权限这种情况错误日志里会有Permission denied。第三种是location匹配错误请求被路由到了错误的location。第四种是try_files配置不当比如SPA应用需要把所有请求fallback到index.html。第五种是反向代理后端返回404这时候要看$upstream_status变量。排查404的步骤先看error.log里的具体错误信息然后确认请求URI和实际文件路径的映射关系再用nginx -T输出完整配置检查location匹配。如果是反向代理场景在日志格式里加上$upstream_status和$upstream_addr一眼就能看出是nginx自己返回的404还是后端返回的。5.2 并发连接数上不去的排查思路“nginx最大并发连接数老是用超”这个问题我遇到过好几次每次原因都不一样。首先要区分是nginx层面的限制还是系统层面的限制。nginx层面看worker_processes和worker_connections的乘积系统层面看ulimit -n和net.core.somaxconn。排查顺序是这样的先用nginx -V确认编译参数然后用ps aux | grep nginx看工作进程数再查/proc/pid/limits看文件描述符限制。如果这些都正常检查error.log里有没有 “worker_connections are not enough” 的警告。还有一个容易被忽略的点是keepalive_timeout设得太长导致连接被长时间占用实际可用连接数下降。# 查看当前nginx进程的文件描述符限制 cat /proc/$(cat /usr/local/nginx/logs/nginx.pid)/limits | grep open files # 查看系统级最大连接数 sysctl net.core.somaxconn sysctl net.ipv4.tcp_max_syn_backlog调优的时候要同步调整系统参数和nginx参数只调一边没用。worker_connections设成10240ulimit -n至少要设成20480somaxconn设成65535。改完系统参数记得sysctl -p生效。5.3 日志分析与状态监控的实用技巧访问日志是排查问题的第一手资料。我习惯用awk和sort做快速统计比如找出访问量最大的IP、响应最慢的URL、状态码分布。这些命令不需要额外工具登录服务器就能跑。# 统计访问量前10的IP awk {print $1} access.log | sort | uniq -c | sort -rn | head -10 # 统计状态码分布 awk {print $9} access.log | sort | uniq -c | sort -rn # 找出响应时间超过3秒的请求 awk $NF 3 {print $7, $NF} access.log | head -20stub_status模块提供的状态页能实时看到活跃连接数、总请求数、读写状态。配置很简单在server块里加一个location就行。但注意这个页面不要暴露到公网只允许内网访问。location /nginx_status { stub_status; allow 127.0.0.1; allow 192.168.0.0/16; deny all; }6. 几个容易踩坑的配置细节6.1 root与alias的本质区别root和alias看起来都是指定文件目录但行为完全不同。root会把location的路径拼接到指定目录后面而alias是直接替换location匹配的部分。举个例子location /img/ { root /data; }请求/img/logo.png会去找/data/img/logo.png而location /img/ { alias /data/; }会去找/data/logo.png。这个区别在配置多级目录的时候特别容易搞混。我的经验是如果location路径和实际文件路径结构一致用root如果不一致用alias。用alias的时候注意末尾斜杠alias /data/和alias /data行为不同前者会把location匹配的部分完全替换后者会保留一部分。6.2 try_files在SPA应用中的正确用法单页应用SPA的路由是前端控制的用户直接访问/user/profile这种路径时服务器上并没有对应的文件。这时候需要try_files把所有找不到的请求都fallback到index.html。location / { root /var/www/spa; try_files $uri $uri/ /index.html; index index.html; }try_files的检查顺序是先找$uri对应的文件再找$uri/对应的目录最后fallback到/index.html。注意最后一个参数必须是内部重定向或者返回码不能是文件路径。如果写成try_files $uri $uri/ /index.html 404那找不到文件时会返回404而不是fallback。6.3 HTTPS证书配置与常见报错HTTPS配置的核心是ssl_certificate和ssl_certificate_key两个指令。证书链要完整中间证书也要包含在ssl_certificate文件里否则部分浏览器会报证书错误。ssl_protocols建议只保留TLSv1.2和TLSv1.3老版本协议有安全风险。server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; }“证书配置错误”这个报错通常有几个原因证书文件路径不对、私钥和证书不匹配、证书链不完整、证书过期。排查的时候用openssl x509 -in cert.pem -text -noout看证书详情用openssl rsa -in privkey.pem -check检查私钥用openssl s_client -connect example.com:443模拟握手过程。6.4 视频流播放的配置要点用nginx做视频点播或者直播转发核心是mp4模块和flv模块。点播场景下mp4模块支持伪流式传输客户端可以拖动进度条而不用下载完整文件。直播场景下flv模块配合HTTP-FLV协议延迟比HLS低很多。location /videos/ { mp4; mp4_buffer_size 1m; mp4_max_buffer_size 5m; root /data; }mp4_buffer_size控制初始缓冲大小mp4_max_buffer_size控制最大缓冲。设太小会导致拖动进度条时频繁回源设太大会占用过多内存。一般1M到5M之间比较合适。视频文件需要先做moov atom前置处理否则伪流式传输不生效用qt-faststart或者ffmpeg -movflags faststart处理一下就行。7. 服务管理方式的选择winsw还是nssm在Windows上跑nginx很多人纠结用winsw还是nssm来做成系统服务。这两个工具我都用过说下实际感受。winsw是.NET写的配置文件是XML格式功能比较纯粹就是把你指定的程序包装成Windows服务。nssm功能更丰富有图形界面能配置日志轮转、进程监控、自动重启。从稳定性来说两者差别不大。winsw的配置文件更直观适合喜欢命令行和版本控制的人。nssm的GUI对新手更友好但配置不好做版本管理。我的建议是如果只是简单地把nginx注册成服务winsw够用了如果需要进程崩溃自动拉起、日志按大小切割这些高级功能nssm更省心。不管用哪个都要注意nginx在Windows上的信号处理跟Linux不同。nginx -s reload在Windows上是通过发送特定事件实现的有时候会不生效。稳妥的做法是nginx -s stop然后重新启动虽然会短暂中断服务但至少行为可预期。8. 我个人的几条实操心得配置文件的版本管理很重要。我习惯把nginx.conf和conf.d/目录一起放到Git仓库里每次改动都有记录出问题了直接回滚。nginx -t这个命令在每次reload之前必须跑一遍它能检查语法错误避免因为一个拼写错误导致整个服务挂掉。日志切割不要用logrotate去reload nginx那样会丢失部分日志。正确做法是给nginx发USR1信号它会重新打开日志文件然后你再移动旧文件。这个细节很多教程都不提但生产环境里日志完整性很重要。最后说一个关于worker_connections的误区。很多人以为设得越大越好实际上每个连接都要占用内存设成100万只会让nginx启动时就OOM。合理的做法是根据实际并发量来定一般10240到65535之间足够了。真正要关注的是keepalive_timeout设得太长会让空闲连接占用资源设得太短又会导致频繁重建连接。我一般设成65秒比大多数负载均衡器的空闲超时略长一点避免连接被意外断开。