ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Nginx下载安装与nginx.conf配置解析:从入门到实践

Nginx下载安装与nginx.conf配置解析:从入门到实践 做Web服务这一行Nginx几乎是绕不开的家伙。无论是个人博客、企业官网还是高并发接口很多流量入口就是它。最近后台常收到私信问Nginx的下载、安装和配置文件解析不少人卡在安装完启动不了或者配置文件改了不生效。这篇干脆把从下载到配置这条线完整走一遍把我平时踩过的坑和验证过的做法都放出来适合刚入门的运维、后端同学也适合想自己搭站点的朋友。先说个大概Nginx的安装方式不少但不管你是编译还是包管理器装最后玩明白的都逃不过那一个nginx.conf配置文件。配置文件一旦理解的框架后面所有虚拟主机、反向代理、负载均衡场景都是在这个框架上做文章。所以这篇文章重点放在“配置文件解析”上安装过程只要照着操作基本都能过配置文件的坑才是真正让人掉头发的。1. 下载Nginx从官网到离线包选型心里要有数1.1 版本怎么选mainline、stable还是老版本Nginx官网提供三类版本页面上一眼就能看到Mainline version、Stable version以及Legacy versions。很多人图新鲜直接选Mainline其实这里有个根本性的取舍。Mainline主线版包含最新特性和bug修复开发活跃适合业务功能需要新模块、或者愿意快速迭代的场景。缺点是更新频繁行为可能有变化生产环境要谨慎。Stable稳定版从主线版中挑选出来的稳定分支功能落定更新频率低应用的兼容性最可靠。绝大多数线上服务器用这个就对了。Legacy历史版本一般没人主动下除非你维护的是一个老系统依赖特定版本行为或者你移植的第三方模块在编译时需要指定版本号。选择思路很简单生产环境默认稳定版折腾环境或要在新内核/新操作系统上验证新特性可以直接主线版。比如我目前常用的稳定版是1.26.x虽然Nginx后来也更新了不少版本但我不会因为版本数字追赶而无脑升级除非有安全公告或者业务确实需要。1.2 官方下载和校验签名下载渠道认准官网就很稳别从网上随便找个镜像站塞到生产环境里。Nginx源码包的下载页面会列出每个版本的tar.gz压缩包和对应的PGP签名文件。拿到包之后别急着解压先做一次完整性校验。官网提供的是.asc格式签名用GPG验证wget https://nginx.org/download/nginx-1.26.2.tar.gz wget https://nginx.org/download/nginx-1.26.2.tar.gz.asc # 导入Nginx官方公钥 gpg --keyserver keyserver.ubuntu.com --recv-keys 520A9993A1C052E8 gpg --verify nginx-1.26.2.tar.gz.asc nginx-1.26.2.tar.gz如果输出里能看到“Good signature”说明包来自官方、没有被篡改。这一步很多人会跳过但放在生产环境我觉得还是有必要的至少能避免下到被投毒的包。校验完之后再解压才算是把“下载”这一关过了。1.3 离线环境或内网怎么准备安装包有时候生产服务器和互联网隔离没法直接wget这时候需要提前准备离线安装包。常规做法有两种有外网的机器上下载好tar.gz源码包以及所有依赖库的源码包然后拷贝到内网编译时装一步算一步。用包管理器缓存机制比如yum的downloadonly插件、apt的apt-get download把所有依赖的.rpm或.deb包拉下来一并拷贝进去再用rpm -ivh *.rpm或dpkg -i *.deb安装。如果你在内网机器上选源码编译需要搞清楚依赖库有哪些PCRE正则支持、zlib压缩、OpenSSLHTTPS/ALPN。这些库最好在下载前就在外网备好版本尽量和系统原有版本兼容。离线编译的时候我习惯用./configure --with-pcre../pcre2-10.42 --with-zlib../zlib-1.3 --with-openssl../openssl-3.0.13这种方式直接指定源码路径比先装系统依赖更可控。2. 安装Nginx源码编译与二进制包两手抓2.1 源码编译安装全过程及依赖问题源码编译是跑Nginx最原教旨的方式好处是能精确控制编译模块缺点是有依赖系统库的坑。我先给出一套在Ubuntu/Debian上的完整流程# 安装基础依赖 apt update apt install -y build-essential libpcre3-dev zlib1g-dev libssl-dev # 解压并进入目录 tar -zxvf nginx-1.26.2.tar.gz cd nginx-1.26.2 # 配置编译参数 ./configure \ --prefix/etc/nginx \ --sbin-path/usr/sbin/nginx \ --modules-path/usr/lib/nginx/modules \ --conf-path/etc/nginx/nginx.conf \ --error-log-path/var/log/nginx/error.log \ --http-log-path/var/log/nginx/access.log \ --pid-path/var/run/nginx.pid \ --lock-path/var/lock/nginx.lock \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --with-stream \ --with-stream_ssl_module # 编译和安装 make -j$(nproc) make install这里--prefix指定的是Nginx运行时根目录不是安装文件前缀后面再详细说目录结构。编译依赖里最容易被漏的是libpcre3-dev漏了会报找不到PCRE库只要提示pcre.h找不到就回头装这个包。make -j$(nproc)是用多核并发编译明显缩短时间。如果系统是CentOS/RHEL依赖命令换成yum install -y gcc gcc-c pcre-devel zlib-devel openssl-devel其他步骤一样。编译完成后nginx -v能输出版本号安装就算OK了。此时还没有注册systemd服务可以用我后面给的systemd service文件来管理。2.2 用Linux包管理器快速安装如果只是想快速跑起来不一定非得编译。CentOS/RHEL自带的yum源里就有nginx但版本可能旧。要装新版建议先添加nginx官方yum源。Ubuntu/Debian:apt update apt install -y nginx安装完直接systemctl start nginx因为包管理器已经帮你处理了启动脚本、目录和默认配置文件。这个方式适合学习或者临时验证默认配置虽然能用但很多指令用不到。CentOS/RHEL:yum install -y nginx安装后默认配置文件在/etc/nginx/nginx.conf和编译安装不同这是发行版整理过的结构会include/etc/nginx/conf.d/*.conf比较适合多站点隔离。如果你准备上一两个生产项目直接用包管理器装再改配置也是很常见的路数。2.3 Windows解压版的目录和注意事项Windows环境里Windows版Nginx经常被用来做本地开发和接口联调。官网提供的是zip压缩包解压即用不用编译。# 解压后进入目录直接启动 nginx.exe # 快速停止 nginx.exe -s stop # 重载配置 nginx.exe -s reload这里最容易出错的是路径和权限在Windows上nginx.conf里的路径要写成C:/nginx/html这种正斜杠反斜杠会被转义或者识别出问题。另外Windows版不支持fork多进程模型性能调优空间很有限本地测试可以生产环境还是老老实实上Linux。3. 配置文件nginx.conf的结构与核心指令解析3.1 配置文件工作原理从全局到location的嵌套关系安装完之后最难啃的就是nginx.conf。Nginx配置的本质是“指令上下文”的树形结构。最外层是main块下面依次嵌套events、http、server、location等。指令的作用域会继承内层可以覆盖外层相同指令。大多数人看完示例配置会困惑“我改listen为什么没起作用为什么server_name匹配不上”本质是没搞清楼层的包含关系。一个请求进来Nginx要做三步根据listen和server_name选一个合适的server块在选定的server块里根据请求URI去匹配location块在location块里执行指定的处理逻辑比如读静态文件或转发给上游。这种“先定server再定location”的思维很重要。配置文件解析问题后面八成都出在这条链路上。3.2 main块和events块中的常用指令main块是顶层配置影响的是Nginx进程本身。最常见的是worker_processes它决定了Nginx启动多少个worker进程。worker_processes auto; worker_rlimit_nofile 65535;auto表示按CPU核心数自动设置。为什么不是设置越多越好因为每个worker都要处理连接、争抢锁进程多了反而降低性能。通常“每CPU核心一个进程”就是经验值。worker_rlimit_nofile是每个worker最多能打开的文件描述符数压测或高并发时调大能减少“too many open files”报错。events块里最关键的是worker_connections定义单个worker能同时保持的最大连接数。理论最大连接数≈worker_processes × worker_connections。但也要考虑系统文件描述符限制events { worker_connections 4096; multi_accept on; }multi_accept on让worker一次接受多个新连接减少惊群效应。这个值不一定要追求极端我一般先设worker_connections 4096压测后根据内存和带宽再调。3.3 http块与虚拟主机server的配置要点http块是整个Web服务的核心存放各种请求处理相关的指令日志格式、超时、压缩、反向代理、负载均衡等。一个nginx.conf可以配置多个server块每个server块就是一个虚拟主机。看一个最精简的serverserver { listen 80; server_name example.com www.example.com; access_log /var/log/nginx/example_access.log; error_log /var/log/nginx/example_error.log; root /var/www/example; index index.html; }listen指定监听端口和地址可以写成80、80 default_server、127.0.0.1:8080。default_server很关键当请求的Host没有匹配到任何server_name时就落到这个默认server上。如果一台服务器上有多个虚拟主机最好把其中一个标记为default_server避免别人用IP撞到第一个server。server_name支持精确域名、通配符*.example.com、正则~^www\d\.example\.com$。匹配优先级是精确域名 左侧通配符 右侧通配符 正则 默认server。刚写的错误往往是精确域名写错比如带了个http://这里只写域名不加协议。http块里另一类重要指令是“调参类型”的sendfile on; tcp_nopush on; keepalive_timeout 65; gzip on; gzip_types text/plain text/css application/json application/javascript;sendfile用来让内核直接发送文件不走应用层拷贝静态文件传输效率高很多。tcp_nopush要在sendfile开启时才有意义它能把多个小包攒成一个包发出去提升网络利用率。gzip压缩的是响应体像HTML、CSS、JS、JSON这类文本压缩率很可观图片视频建议别开。3.4 location匹配规则顺序、优先级和常见误区location是配置解析里最考验基本功的部分。它有四种匹配方式精确匹配优先级最高。^~前缀匹配一旦命中就不再检查正则。~正则匹配区分大小写~*不区分大小写。普通前缀匹配直接写路径优先级最低。匹配顺序简单说先精确匹配没命中再看前缀匹配记录下最长匹配的前缀如果有^~命中了就直接用否则继续按顺序测试正则命中第一个就采用正则都没有才用之前最长普通前缀。举个例子location /favicon.ico { log_not_found off; } location ^~ /static/ { root /var/www/static; } location ~* \.(gif|jpg|png)$ { root /var/www/images; } location / { proxy_pass http://backend; }请求/static/app.js会命中^~ /static/不再执行后面的正则请求/cat.jpg会命中~* \.(jpg)$请求/foo/bar命中location /。最容易踩的坑是路径加没加/。location /static和location /static/不同前者能匹配/staticf这样的路径后者只能匹配以/static/开头的。如果本来想限制静态目录、却漏了末尾斜杠可能出现非预期访问。3.5 upstream负载均衡和反向代理的proxy_pass细节反向代理是Nginx承担流量入口的必修课。核心逻辑是客户端请求到NginxNginx再接后端服务拿到响应后回给客户端。先看负载均衡的上游定义upstream backend_api { least_conn; server 192.168.1.10:8080 weight5 max_fails2 fail_timeout30s; server 192.168.1.11:8080 weight1 backup; keepalive 32; }least_conn表示按后端活跃连接数分配比默认的轮询对长连接服务更友好。weight是权重只有backup标记的后端在所有非backup节点都挂掉后才接收请求。keepalive是Nginx与后端之间的长连接数量减少重复握手开销。转发时用proxy_passserver { listen 80; server_name api.example.com; location /api/ { proxy_pass http://backend_api; 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_pass后面有没有URI。如果proxy_pass http://backend_api;后面不带路径转发时会把完整原始URI传给后端如果写成proxy_pass http://backend_api/;后面带了/则会用这个路径替换location /api/的前缀部分。假设请求是GET /api/user?id1proxy_pass http://backend_api;后端收到/api/user?id1proxy_pass http://backend_api/;后端收到/user?id1这两种行为差之毫厘谬以千里。实际配置时一定要根据后端接口的路由设计选择带不带斜杠。4. 真实场景配置演示静态站点、反向代理和负载均衡4.1 场景一发布一个静态网站并开启gzip假设你在/var/www/myblog目录下放了一个静态博客要有域名example.com访问。先建一个server配置文件推荐放在独立目录管理编译安装的话可以自己新建/etc/nginx/conf.d/myblog.confserver { listen 80; server_name example.com www.example.com; root /var/www/myblog; index index.html; gzip on; gzip_min_length 1k; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_proxied any; location / { try_files $uri $uri/ 404; } location ~* \.(png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 30d; add_header Cache-Control public, immutable; log_not_found off; } }try_files $uri $uri/ 404的意思是先尝试找真实文件找不到就尝试找目录再没有就返回404。这个配置能防止纯路径请求落到其他资源上。图片类location设置expires 30d让浏览器缓存一个月减少重复请求。改完配置先检测nginx -t输出syntax is ok和test is successful之后执行nginx -s reload如果浏览器还看不到新效果可能是浏览器缓存强制刷新一下基本就好了。4.2 场景二反向代理后端的Node/Python服务现在很多后端服务跑在http://127.0.0.1:3000不想直接暴露端口就让Nginx监听80端口并转发到3000。server { listen 80; server_name api.example.com; access_log /var/log/nginx/api_access.log; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; 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_http_version 1.1和Connection upgrade这两行对WebSocket很重要。如果后端是Socket.IO或实时服务没有这两行会频繁断连。普通HTTP接口可以不写但建议保留。后端服务如果响应慢可以在location里加超时控制proxy_connect_timeout 10s; proxy_read_timeout 30s; proxy_send_timeout 30s;注意这些是Nginx和后端之间的超时不是浏览器和Nginx之间。我之前遇到过前端等不到响应调大浏览器超时没用其实是后端响应超过了Nginx的30秒限制。4.3 场景三两个后端实例做负载均衡假设你有两个Node服务实例分别跑在127.0.0.1:3001和127.0.0.1:3002准备用Nginx统一接入。同上先定义upstreamupstream node_cluster { least_conn; server 127.0.0.1:3001 weight2; server 127.0.0.1:3002 weight1; keepalive 64; }然后server块里转发server { listen 80; server_name app.example.com; location / { proxy_pass http://node_cluster; proxy_http_version 1.1; 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会自动把请求发给还存活的上游节点。max_fails和fail_timeout控制健康检查判断server 127.0.0.1:3001 weight2 max_fails3 fail_timeout10s;意思是10秒内失败3次就标记该节点不可用之后10秒内不再转发给它等冷却时间过了再尝试。这里有一个经验点不要把失败次数设得太小像1次就摘掉节点可能接收短时间抖动但设太多又会导致大量请求被打到故障节点。通常max_fails2、fail_timeout10s是稳妥起点。4.4 让配置生效reload、restart与日志排查改了配置文件不是重启越频繁越好。Nginx支持平滑重载nginx -s reload它会用新配置启动新的worker再把旧worker进程慢慢退出对在线用户影响很小。相比之下systemctl restart nginx或nginx -s stop再start会中断所有连接生产环境要避免频繁这么干。排查问题时日志是最好用的线索error.log记录启动、连接、转发错误。access.log记录每个请求的访问信息。我习惯在server块里单独指定日志路径不然所有站点信息会堆到全局日志里不好分清业务来源。access_log /var/log/nginx/myapp_access.log; error_log /var/log/nginx/myapp_error.log;日志用不上时可以开access_log off;减少磁盘写压力。要观测实时请求tail -f /var/log/nginx/myapp_access.log看到状态码和响应耗时比在那里猜要快得多。5. 常见问题与排查技巧实录5.1 nginx -t配置检测与常见报错配置文件写错是最常见的“看起来运行正常但行为不对”的元凶。每次改完配置我强迫症一样先跑一遍nginx -t如果输出nginx: [emerg] unknown directive xxx in /etc/nginx/nginx.conf:12八成是少写了分号或者指令名拼错。Nginx配置每一行指令都必须以分号结束块内大括号{}后面不需要分号但指令结尾要分号。还有一种隐蔽错误文件的换行符是Windows的\r\n从Windows下编辑过再传上去运行时会unknown directive看起来指令名没错其实多了个\r字符。用sed -i s/\r$// nginx.conf去掉或者用Linux编辑器重存一次。5.2 端口占用、bind失败怎么处理启动时报错nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)说明80端口已经被占用。先查占用进程ss -lntp | grep :80 # 或 lsof -i :80如果是Nginx自己占着可能是之前启动过没停止。按顺序执行nginx -s stop # 优雅停止 # 或者 pkill -9 nginx # 强制杀掉再启动就好了。如果没有kill干净可以用systemd管理Nginx这样进程状态和开机自启都好控制。给编译安装的Nginx写个service文件[Unit] Descriptionnginx - high performance web server Afternetwork-online.target Wantsnetwork-online.target [Service] Typeforking PIDFile/var/run/nginx.pid ExecStart/usr/sbin/nginx ExecReload/usr/sbin/nginx -s reload ExecStop/usr/sbin/nginx -s stop Restarton-failure RestartSec5s [Install] WantedBymulti-user.target保存到/etc/systemd/system/nginx.service然后systemctl daemon-reload systemctl enable nginx systemctl start nginx这样以后就能用systemctl reload nginx平滑重载了。5.3 权限403和静态资源404的排查思路访问静态文件时浏览器返回403大概率是权限或路径不对。先看Nginx的动态用户有没有权限读取文件目录user www-data;编译安装默认可能是user nobody;需要确保该用户对网站目录有读取权限。一个很稳的方法是把目录归属给www-data权限设置为755文件设置为644。404的问题则往往是root路径写错或者文件本身不存在。用curl确认curl -I http://127.0.0.1/如果返回404去服务器上看文件ls -l /var/www/myblog/index.html注意root规则是“把location路径直接拼到root路径后面”。比如root /var/www/myblog;请求/assets/app.js实际找的是/var/www/myblog/assets/app.js。如果你写的root没有考虑到请求前缀也会404。5.4 “运行正常但代理失败”的坑与排查顺序反向代理场景里最大的坑是“浏览器打开了503或502”。502表示Nginx连不上后端503可能是upstream里没可用节点。按经验排查顺序后端服务是否在监听正确端口curl http://127.0.0.1:3000/health。防火墙或云安全组是否放通Nginx到后端如果是跨机器需要确保网络可达。Nginx配置的proxy_pass是否拼写正确、端口是否一致。看error.log是否有connect() failed (111: Connection refused)如果有就是后端没监听或监听地址不对。看是不是有upstream timed out如果是检查后端响应时间是否太长调整proxy_read_timeout。还有一个经常被忽略的点DNS解析。如果你的upstream里的server写的是域名比如server api.internal.example.com:8080;Nginx启动时会先解析一次之后不会自动刷新。域名解析变了要reload才会生效。如果后端IP频繁变化建议直接写IP或者在http块里使用resolver指令配合变量不过这个属于进阶用法新手先注意别被这个坑绊住。多数“配置了代理但不生效”到最后基本都是网络或后端没起来而不是Nginx语法问题。别一上来就怀疑Nginx转发逻辑先从头到尾把链路捋一遍。实际过程中我习惯在server块临时加一个return 200 health;测试这个server能否正常访问再去逐层排除后端比闷头改配置高效得多。我个人在实际操作中还养成了一个习惯nginx -t检测通过后一定先备份当前配置再reload出了问题可以一秒回滚。配置拆分也用得越来越细把server块拆到/etc/nginx/conf.d/下每个站点一个文件找起来不用在一大坨nginx.conf里翻。Nginx配置文件并不复杂怕的是把各种逻辑揉在一起还不加注释。写配置解析时顺手给每个server块加两行注释说明用途三个月后回来看你会感谢自己。
返回列表