1. 为什么Web项目上线绕不开Nginx
1.1 从“本地跑通了”到“外网能访问”,中间缺的那一环在哪
我见过太多这样的场景:开发机上Spring Boot项目跑得好好的,浏览器输入localhost:8080一切正常,一旦部署到服务器,用户反馈“打开一片空白”“图片加载一半就断”“接口时不时超时”。代码没变,数据库没动,问题几乎都出在Web项目与网络环境部署的衔接层上。
在真实的网络环境部署里,一个Web项目从本机到外网可访问,至少要穿过这几道关卡:服务器操作系统、防火墙、公网端口、域名解析、安全策略。这中间真正承担“路由”和“分发”任务的,往往不是你的Web应用本身,而是部署在应用前面的Nginx。
Nginx到底是什么?用大白话说,它是一个高性能的Web服务器,也是一个反向代理和负载均衡工具。它可以在80端口和443端口上接收来自用户的HTTP请求,然后决定:这个请求要不要直接返回静态页面,要不要转发给后面的Java/Python/Node进程,要不要按负载策略分给其中一台服务器。
这里有个很关键的认知:你的Web项目本身只能用localhost:8080这种形式监听本机端口,它并不知道外面世界长什么样。Nginx站在公网入口,替你的项目做“第一道接待”。用户访问的是Nginx,Nginx再把请求小心地送到后台项目手里。所以,凡是涉及正式上线的Web项目,几乎没有一个绕得开Nginx。
1.2 静态服务、反向代理、负载均衡:Nginx的三大看家本事
用生活里的场景来类比会更好理解。
假设你开了一家餐厅。客人进门先找服务员。这位服务员要看客人是什么类型:如果客人只是取个外卖(静态资源),服务员直接去打包台拿外卖递给他;如果客人要坐下吃饭(动态接口),服务员把客人带到对应餐桌,让后厨去炒菜;如果客人非常多,服务员还需要把客流分散给不同的分店同时处理,这就是负载均衡。
Nginx就是那位极为高效的服务员。它最擅长的三件事包括:
第一,静态文件服务。图片、CSS、JS、PDF、视频这些不需要后端计算的文件,Nginx可以直接从磁盘返回,速度比任何应用服务器都快,而且几乎不占用后端资源。热搜词里的“nginx共享文件”,本质上就是用root或alias指令把一个目录挂在HTTP路径下。
第二,反向代理。Nginx收到请求后转发给内部的Web服务,再把Web服务的响应原样交还给用户。用户永远只看到Nginx,看不到你真正的后端IP和端口,这层“遮挡”本身就是一种安全保护。
第三,负载均衡。当你的项目部署在多台服务器上时,Nginx可以通过upstream配置把请求按轮询、权重、IP哈希等方式分发到不同节点,既提升吞吐,又能在某个节点挂掉时自动避开。
1.3 什么时候可以不用Nginx,什么时候必须上
不是所有场景都需要Nginx,我一般按这个标准来判断:
如果只是公司内网临时测试,三五个同事访问一个localhost:8080,直接跑Spring Boot或者Node就够,没必要多加一层。如果是一个写来玩的小Demo,只有一个页面,也可以直接python3 -m http.server。
但只要有下面任何一个条件,就应该认真考虑上Nginx:
- 项目需要同时承载静态资源访问和动态接口,且静态资源量不小;
- 需要绑定域名并启用HTTPS,443端口的TLS终结适合放在Nginx;
- 一台服务器上要部署多个Web应用,需要用不同域名或不同路径区分;
- 需要反向代理隐藏后端细节,或者在后端前面做统一缓存、限流、日志;
- 后端服务不止一个实例,需要负载均衡。
说白了,Nginx承担的是一个“网关”角色。Web项目规模越大、网络环境部署链路越复杂,它的价值就越突出。
2. 部署前的环境准备:从零装好一台能用的Nginx
2.1 系统选型与基础环境检查
这篇文章里的实操我以AlmaLinux 9为例,它和Rocky Linux、CentOS Stream等系统操作基本一致,如果你用的是Ubuntu/Debian,命令会有一点差别,关键是理解思路。
装Nginx之前,先花几分钟把系统状态摸清楚。首先是确认系统版本:
cat /etc/os-release然后确认80和443端口没有被占用:
ss -lntp | grep -E ':80|:443'看到任何输出都代表端口被占。常见占用者是httpd、另一个Nginx,或者某个自己监听80端口的服务。如果有不认识的进程占着,先搞清楚是什么,别急着kill。
接着检查防火墙。AlmaLinux 9默认使用firewalld,要提前确认规则:
firewall-cmd --state如果系统刚装完,很可能还没有放行HTTP服务。这一步放到后面启动Nginx之后再处理可以直接对比效果。
2.2 三种安装方式,怎么选才不后悔
网上关于“nginx安装”的教程满天飞,但我建议你先想清楚自己需要哪种安装方式。三种方式各有适用场景。
第一种,操作系统自带软件源安装。在AlmaLinux上是:
dnf install -y nginx优点是快、稳、依赖自动解决,systemd服务脚本全是配好的。缺点是版本通常偏旧。如果你只是做常规部署,不追求最新特性,这是最推荐的默认选择。
第二种,添加Nginx官方源安装。官方源:
dnf install -y https://nginx.org/packages/centos/9/x86_64/RPMS/nginx-release-centos-9-0.el9.x86_64.rpm装好之后就能从官方仓库拿到比较新的稳定版。这个方法适合想要新特性(比如HTTP/3支持)又不想自己编译的人。注意,既然装了官方源版本,就不要同时混用系统自带版本,容易冲突。
第三种,源码编译安装。流程大致是:
dnf install -y gcc pcre-devel zlib-devel openssl-devel wget https://nginx.org/download/nginx-1.27.3.tar.gz tar -zxvf nginx-1.27.3.tar.gz cd nginx-1.27.3 ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf make && make install编译安装最大的好处是可以按需裁剪模块,比如只保留必要的模块、定制安装路径,这在容器镜像里会用到。但日常使用我不太推荐它:升级麻烦,依赖也是手动管理,版本一旧就变成历史包袱。
2.3 启动、开机自启与验收
用包管理器安装完Nginx之后,第一件事不是急着改配置,而是先把服务拉起来看能不能正常工作:
systemctl start nginx systemctl enable nginx systemctl status nginx然后在服务器本地验证:
curl -I http://127.0.0.1正常情况下会返回一个HTTP 200响应,页面内容是一句简短的“Welcome to nginx!”。
这时候从外部浏览器访问服务器公网IP,大概率还是打不开。别慌,这是防火墙没放行端口:
firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https firewall-cmd --reload如果你用的是云服务器,除了系统防火墙,还要去云控制台的安全组规则里放行80和443端口。很多新手在这里栽跟头:系统里curl明明通了,外面就是打不开,最后发现是安全组规则漏配了。这一步优先检查。
搞定这些,你的服务器上已经有一个能跑的Nginx了。接下来才是真正的核心:把配置文件搞清楚。
3. 扒开nginx.conf:配置文件的骨架与核心指令
3.1 配置文件的层级关系,先有全局观
Nginx的配置文件入口通常是/etc/nginx/nginx.conf。它看起来很长,但拆开看结构非常清晰:最外层叫main配置块,里面依次是events块、http块、http块下的server块,以及server块下的location块。
| 配置块 | 作用范围 | 典型指令 |
|---|---|---|
| main | Nginx进程全局行为 | user、worker_processes、error_log、pid |
| events | 连接处理方式 | worker_connections、use epoll |
| http | 所有HTTP请求的公共设置 | include、default_type、gzip、log_format |
| server | 一个网站/虚拟主机的行为 | listen、server_name、root、ssl证书 |
| location | URL路径级的行为 | alias、proxy_pass、try_files、autoindex |
理解这个层级的关键在于“继承”思想:http块里的配置会作为所有server块的默认值,server块里的配置会作为其中所有location的默认值,内层可以覆盖外层。
3.2 一个能跑的静态网站配置,照着抄再改改
假设我把一个纯前端项目放到/data/web/blog目录下,想通过blog.example.com访问。一个最小的server配置长这样:
server { listen 80; server_name blog.example.com; root /data/web/blog; index index.html; location / { try_files $uri $uri/ =404; } }每个指令的意图都很明确:listen 80表示监听IPv4的80端口;server_name用来匹配Host请求头,多个域名用空格隔开;root表示站点根目录,注意这里是目录,不是某个文件;index指定默认主页;location /表示对所有路径生效;try_files $uri $uri/ =404的意思是,先找对应文件,再找对应目录,都没有就返回404。
如果你想做“nginx共享文件”这种功能,只需要加两行:
location /files/ { alias /data/shared/; autoindex on; }autoindex on开启目录列表,用户访问/files/就能看到目录下的所有文件。alias和root的区别一定要记清楚:root /data/web加location /blog会映射到/data/web/blog,而alias /data/web加location /blog会映射到/data/web。alias的替换更彻底,用错了路径必乱。
3.3 location匹配规则:别再被“/”和“/xxx”搞晕
location是Nginx配置里最容易被初学者踩坑的地方。它的匹配规则分为几种:
location = /xxx:精确匹配,完全等于才生效;location ^~ /static/:前缀匹配,一旦命中就不再继续找正则;location ~ \.php$:正则匹配,区分大小写;location ~* \.jpg$:正则匹配,不区分大小写;location /:通用前缀,兜底。
匹配时有一个优先级顺序:精确匹配(=)大于前缀匹配(^~)大于正则匹配(~、~*)大于通用前缀。本质上,Nginx先在所有前缀匹配里逐个试,如果命中^~就直接用;否则继续按定义顺序试正则,正则里先命中的胜出。
我实际开发中遇到最多的问题就是:明明配置了location /api,结果/api/v1/users也匹配到了另一个正则location末尾的文件路径规则,导致请求被错误地当成静态文件处理。解决办法很简单:把需要优先的写成location ^~ /api { ... }。
4. 反向代理实战:把Web项目正式接到Nginx上
4.1 反向代理到底“代”了什么
反向代理的概念看似高深,拆开看并不复杂。我在上一节已经用餐厅服务员做了比喻,具体到技术上:当用户请求到达Nginx的80端口,Nginx根据规则把整个请求转发到内网某个端口,比如说127.0.0.1:8080上的Spring Boot进程,后端处理完毕后再原路返回。
这个过程用户完全感知不到。他始终只看到blog.example.com这个域名,看不到8080端口,也看不到真实的后端IP。这正是反向代理的意义:对外是一个统一入口,对内是一套灵活的转发策略。
4.2 Spring Boot、Node等后端的标准接入方式
先看一个最常用的Spring Boot项目接入配置:
server { listen 80; server_name api.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; proxy_set_header X-Forwarded-Proto $scheme; } }proxy_pass是核心指令,写法和结尾要不要带斜杠直接影响转发结果。如果proxy_pass http://127.0.0.1:8080;不带路径,Nginx会把完整原始URI转发过去。如果变成proxy_pass http://127.0.0.1:8080/;,则会把location中匹配到的部分替换掉。很多团队文档里都没写清这个细节,导致转发后接口404,排查半天。
proxy_set_header的作用同样重要。Host $host带上原始域名,后端做多租户或域名判断时需要;X-Real-IP和X-Forwarded-For用于把真实客户端IP传给后端,不然后端日志里全是127.0.0.1;X-Forwarded-Proto让后端能知道用户是通过HTTP还是HTTPS访问的。
Node.js项目(比如Express)接入方式完全一样,只需要把proxy_pass的地址改成http://127.0.0.1:3000即可。前端部署在Nginx、后端接口由同一域名转发时,通常会把静态资源放在一个location里,接口放在另一个location:
location / { root /data/web/frontend; try_files $uri $uri/ /index.html; } location ^~ /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; }前面那个try_files ... /index.html是给前端路由用的:访问/about时没有这个物理文件,就会回退到/about被前端框架接管后渲染对应页面。
4.3 几个让人头疼的代理细节
反向代理成功跑通只是第一步,线上常见的是下面这些细节问题。
WebSocket代理。热搜词里有“spring boot集成websocket yml配置”,顺带提一下Nginx端配合WebSocket时,光靠普通proxy_pass是不够的,需要额外加两行:
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s;Upgrade和Connection是WebSocket协议升级的核心头,不加的话浏览器和后端始终建立不了长连接。proxy_read_timeout也要调大,默认60秒不够用,否则长连接中途会被Nginx掐断。
客户端上传大小。Spring Boot默认限制单次请求大小,Nginx也有自己的限制,默认是1MB。如果项目里要上传大文件:
client_max_body_size 50m;这个指令放在http、server或location块里都可以。忘了设置的话,上传超过1MB的文件会直接返回413错误,而且后端日志里什么都看不到。
502和504是最常见的反向代理错误。502的意思是Nginx连接不上后端,后端的监听地址写错、进程挂了、端口没起来都有可能。504是连接上后端了但响应超时,通常要调大proxy_read_timeout。
4.4 负载均衡:多节点部署时的关键字
当你的Web项目要扛更大流量时,单机总会有上限。Nginx做负载均衡的配置非常简单:
upstream backend_nodes { server 192.168.1.10:8080 weight=3; server 192.168.1.11:8080 weight=1; keepalive 32; } server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_nodes; proxy_http_version 1.1; proxy_set_header Connection ""; } }这里用一个upstream块把两台后端服务器封装成一组,proxy_pass指向这个组,请求就会自动分发。weight=3表示第一台机器承受的流量是第二台的三倍,适合性能不等的服务器。
如果后端的接口涉及用户会话状态,比如登录状态存在某个节点的本地Session里,可以在upstream里加一句ip_hash;,让同一个IP的请求始终打到同一台服务器。这算一种简易的会话保持方案,但不是最终的解决办法。真正的分布式Session还得靠Redis这类外部存储,Nginx只能做“出入口调度”,替不了业务层。
5. HTTPS、Web安全与线上加固
5.1 正规部署必须过HTTPS这关
现在浏览器对HTTP的“不友好”已经非常明显:Chrome会在地址栏直接标“不安全”,微信内置浏览器对某些HTTP页面有限制,几乎所有的登录接口也建议只在HTTPS下传账号密码。所以任何正式上线的Web项目,第一步就是要把HTTP升级成HTTPS。
HTTPS的核心是证书体系。你需要在服务器上配置一对密钥,证书文件(example.com.pem)和私钥文件(example.com.key),然后在Nginx里启用SSL。证书来源有三个方向:付费商业证书、免费DV证书(Let's Encrypt等)、开发阶段的自签名证书。
如果是个人学习、内网测试,自签名证书就够了。线上产品则推荐用免费DV证书或成本更高的OV/EV证书。申请证书的门槛很低,关键在于让Nginx正确使用它。
5.2 证书配置与强制跳转
拿到证书后,在server块里启用HTTPS:
server { listen 443 ssl; http2 on; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.pem; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; root /data/web/example; index index.html; }我习惯为项目设定如下护栏式要求:只启用TLSv1.2和TLSv1.3,禁用老旧的TLSv1.0/1.1,因为它们已被证明存在严重漏洞。ssl_ciphers保持默认安全套件即可,不要把网上随便搜到的“高性能配置”原样贴过来,有些配置会牺牲兼容性。
HTTP跳转HTTPS的配置有两种写法。推荐用单独的80端口server块:
server { listen 80; server_name example.com; return 301 https://$host$request_uri; }这样所有访问http://example.com的请求都会返回301,浏览器自动跳到https://example.com对应的完整路径。如果你担心搜索引擎的收录问题,301是正确做法,302不利于SEO。
5.3 Nginx层面的安全习惯
安全加固不是安全工程师的专属工作,做Web部署的人都应该养成的习惯。我每次上生产环境,都会在配置里做四件事。
第一件,隐藏版本号。在http块顶部设置:
server_tokens off;不关的话,任何请求出错时Nginx都会在错误页和响应头里泄暴露具体版本,攻击者可以直接按版本搜索历史漏洞。
第二件,加上常见的响应安全头。在server块里设置:
add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;X-Frame-Options防止页面被恶意站点用iframe嵌入,降低点击劫持风险;X-Content-Type-Options防止浏览器做MIME类型嗅探;Referrer-Policy控制跳转时携带哪些来源信息。
第三件,禁止访问明显敏感的文件。很多Web项目即使没有部署好,/.git/目录和.env文件也可能留在项目目录里。用一段location规则挡掉:
location ~ /\.(git|svn|env|idea) { deny all; }这个正则的意思是:任何路径中带有.git、.svn、.env等文件目录的请求,一律拒绝。
第四件,限制允许的请求方法。如果项目只需要GET和POST,就没必要允许DELETE、PUT从外部随意进来:
if ($request_method !~ ^(GET|POST|HEAD)$) { return 444; }这里返回444是Nginx特有的非标准状态码,表示“直接关闭连接不放任何内容”,比返回403更安静,因为连错误信息都不给。
6. 高并发与性能调优:让Nginx撑住更多请求
6.1 动手调优之前,先搞清楚瓶颈在哪
很多人一听说高并发就急着改worker_processes,其实不对。Nginx能扛多少并发,核心取决于两个指标:进程数和每个进程允许的连接数。两者相乘得到的最大连接数是一个上限,但真正能达到多少,还要看服务器的CPU核数、内存、网络带宽以及后端的处理能力。
在后端业务没优化、数据库慢查询一堆的情况下,调Nginx参数只是延长了请求排在队列里的时间,并不会提升吞吐量。所以调优第一步永远是监控,先用top、free -h、ss这类命令看看瓶颈在哪里,再回头动配置。
6.2 核心参数怎么调才算合理
下面这组参数是我在多数Linux服务器上的起步配置,方向和数值都可以当参照。
worker_processes auto; events { worker_connections 10240; use epoll; multi_accept on; } http { keepalive_timeout 65; keepalive_requests 1000; gzip on; gzip_types text/css application/javascript application/json image/svg+xml; gzip_min_length 1k; }worker_processes auto会按CPU核心数自动创建worker进程。Nginx是事件驱动模型,多进程的好处是能利用多核CPU;一个核心配一个worker通常就是最合理的。worker_connections表示每个worker最多同时保持的连接数,4096到10240是常见区间,但要和系统文件句柄配合,先执行ulimit -n确认上限够大。
use epoll是Linux环境下的高效事件处理模型,默认就会启用,不用刻意写。multi_accept on让每个worker一次接受尽可能多的新连接,有利于突发流量下的响应速度。
gzip压缩对文本类资源的收效非常明显。CSS、JS、JSON这类资源往往能压缩掉70%-90%的体积,意味着用户加载速度大幅提升。但注意,图片(JPEG、PNG、WebP)本身已有压缩,再开gzip会徒增CPU开销,所以不要无脑把所有类型都开上。gzip_min_length 1k保证太小的响应不压缩,避免压缩开销大于传输省下的时间。
6.3 静态资源缓存策略
缓存是Web性能的另一半战场。Nginx可以通过expires让浏览器直接缓存静态文件,减少重复请求:
location ^~ /assets/ { expires 7d; add_header Cache-Control "public, immutable"; }expires 7d会给响应头带上一个有效期七天的Expires字段。immutable的含义是:这个文件在这段时间内不会变,浏览器甚至不需要重新验证,直接使用本地副本。
前端项目经过Webpack等工具打包后,文件名通常带着内容哈希,比如app.8f3d2a.js。这种文件非常适合设置很长时间的缓存。但入口index.html不能长缓存,否则用户发新版后浏览器还在用旧版索引,导致加载的还是老资源。所以标准做法是:index.html配置成不缓存,带哈希的静态资源配置成长缓存。
6.4 反向代理连接调优
如果Nginx后面挂着后端,还要注意代理侧连接参数。比较实用的是开启上游keepalive,避免每个请求都新建TCP连接:
upstream backend_nodes { server 127.0.0.1:8080; keepalive 64; } server { location /api/ { proxy_pass http://backend_nodes; proxy_http_version 1.1; proxy_set_header Connection ""; } }keepalive 64表示Nginx缓存与后端之间的64个空闲连接。proxy_http_version 1.1和proxy_set_header Connection ""是让后端支持HTTP/1.1长连接的必要条件。这个配置对短平快的接口能带来非常明显的性能提升,因为省掉了一堆TCP三次握手和四报文挥手的开销。
7. 平滑升级与故障排查实录
7.1 改配置不中断服务的基本操作
很多人第一次改Nginx配置,习惯性是改完后直接systemctl restart nginx。虽然这样也能生效,但线上的服务会瞬间中断,对正在处理中的请求极不友好。正确的做法是平滑重载。
改完配置文件后,先做语法检查:
nginx -t这个命令会告诉你配置文件有没有语法错误。输出两种结果,成功时显示:
syntax is ok test is successful如果有问题,会直接告诉你错在哪个文件的第几行,按提示改就行。确认无误后执行:
nginx -s reloadreload的主要工作方式如下:Nginx的master进程收到reload信号后,会重新加载配置文件并启动新的worker进程,然后等旧的worker进程处理完手头请求再慢慢退出。整个过程对外几乎是零中断。
当二进制版本要升级时,也可以做平滑升级:先编译或下载新版本,执行make upgrade或手动向master进程发送USR2信号再快速切换新进程。不过这个操作日常用得不多,真正重要的是养成“先nginx -t再reload”的习惯。我自己上线流程里永远都是这条纪律:配置备份,然后测试,最后reload,绝不直接restart。
7.2 高频排查链路:403、502、504、端口占用
线上部署遇到最多的问题就这几种,我把排查链路理成清单,方便你对着走。
403 Forbidden。先看Nginx错误日志:tail -f /var/log/nginx/error.log。403的原因通常是这四类:目录或文件权限不对,Nginx的工作用户(默认是nginx用户)没有权限读取文件;index指定的文件不存在;SELinux在AlmaLinux系系统上拦了Nginx访问指定目录;前面提到的deny all等安全规则命中了请求。排查顺序建议:先在curl里看错误日志,再用ll看文件权限,再用getenforce看SELinux状态。SELinux如果是Enforcing,临时测试可以用setsebool -P httpd_can_network_connect 1这类策略调整,但不建议直接关闭。
502 Bad Gateway。最常见的情形是后端进程没起来,或者proxy_pass的端口写错。确认方法是在服务器上直接访问后端地址:curl -I http://127.0.0.1:8080。如果本地能通,再检查Nginx的worker用户能否跨网络访问,某些云主机默认收紧了出口访问,也会导致Nginx向上游连接失败。还有一个冷门但经常踩的原因:upstream配置了域名,但Nginx解析域名时缓存了旧的IP,域名迁移后旧IP连不上就一直是502。这种情况可以给upstream的server指令加resolve参数,或者直接使用IP地址。
504 Gateway Timeout。这个比502好定位,意思是后端接收了请求但处理时间超过了Nginx等待的时限。先看后端日志里有没有长时间运行的SQL或慢接口,再看Nginx的proxy_read_timeout和proxy_send_timeout是否被设置得太短。常规接口建议设60秒,批量导出类接口设到300秒也不奇怪,不要用一套值打天下。
80端口被占。这个在2.1节提过,用ss -lntp | grep :80就能看到是谁。如果存在另一个Nginx进程,可以检查是否重复启动;如果是httpd或Tomcat在监听,需要在它们各自配置里把监听端口改掉或者停掉,不要强行和Nginx抢端口。
还有一个我经常帮别人排查的问题:配了server_name后,用IP访问返回404或默认站点页面,但用域名访问却正常。这不是故障,而是Nginx的虚拟主机机制:不同server块靠server_name匹配Host头。IP访问时Host头就是IP,自然匹配不到你配置的域名。如果希望IP访问也走某个server块,在listen后加default_server即可。
7.3 日志分析:从access.log和error.log里找真相
这是一个很朴素的习惯:部署过程中遇到任何奇怪问题,第一眼不要猜,先去看日志。Nginx默认把访问日志写到/var/log/nginx/access.log,错误日志写到/var/log/nginx/error.log。
访问日志里最关键的是每行末尾的几个字段。以默认的combined格式为例:
192.168.1.50 - - [10/Nov/2025:14:22:01 +0800] "GET /api/users HTTP/1.1" 200 1256 "http://example.com/users" "Mozilla/5.0"状态码是200那当然没事;如果是304说明命中了浏览器缓存;如果是404,就要看你请求的路径和root/alias组合是否正确;如果是500,问题几乎都在后端,Nginx只是原样转发了后端的状态码。
错误日志的格式更直接,会写明是配置问题、权限问题还是连接超时。我遇到过最典型的案例是前端部署时目录名写错,页面所有请求全部404,错误日志一行一行地显示“directory index of /data/web// is forbidden”。看到这种告警,第一反应就是去检查root路径后有没有多余斜杠。
日志还有一个用途:判断你有没有被恶意扫描。线上服务器经常会被陌生IP扫描器访问奇怪路径,比如/.env、/wp-admin、/phpmyadmin。看到这些也不要慌,只要安全规则挡得住,它们就只是背景噪音。当然,定期翻一翻日志能帮你尽早发现异常流量模式。
这里再分享一个排查“只能本地访问不能局域网访问”的通用思路。遇到过路由器端口没做映射、云服务器安全组没放行、项目本身只监听了127.0.0.1而不是0.0.0.0等多种情况。用Nginx做入口时,你只需要保证Nginx监听0.0.0.0:80(默认配置就是如此),然后逐层验证:本地curl通,说明Nginx正常;局域网另一台机器不通,检查本机防火墙;外网不通,检查云安全组和路由器映射。按这个链路一层层排除,通常十分钟就能定位。
我个人在实际操作中最深的体会是:Web技术与Nginx网络环境部署这件事,真正难的不是某个配置指令记不记得住,而是当请求链路出现问题时,你有没有一套清晰的排查路径。Nginx配置指令总能查到,方法论却要靠一次次排错积累。每次改配置前先备份,每次上线前先nginx -t,遇到问题先翻日志再动脑子,这套习惯帮我躲过了线上很多次本可避免的事故。
最后再分享一个小技巧:把你的核心配置片段拆成独立文件放到/etc/nginx/conf.d/或者/etc/nginx/snippets/目录里,一个站点一个文件,这样维护和回滚都很方便。等你对这套流程跑熟了,就会发现Nginx不仅是Web项目的入口,更是一面帮你挡掉大量低级问题的安全屏障。