2016年,我在部署一个PHP项目时,被Nginx的配置折腾了很久。

那时候我对Nginx的了解还停留在"能跑就行"的阶段——复制网上的配置,改改域名和端口,能访问就觉得OK了。但是真正部署项目的时候,才发现Nginx的配置有很多坑——502 Bad Gateway、404 Not Found、伪静态不生效、Gzip不压缩、HTTPS配置错误、负载均衡不生效、缓存不生效……

那段时间,我经常熬夜排查Nginx配置问题,查文档、翻论坛、做实验,踩了不少坑,也积累了不少经验。

今天就来聊聊,那些让我熬夜的Nginx配置坑,以及我总结的排查和解决方法。

一、Nginx是什么

在聊坑之前,先简单说说Nginx是什么。

Nginx(发音"engine-x")是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。它由俄罗斯的Igor Sysoev开发,第一个公开版本发布于2004年。

Nginx的特点:

  • 高性能:事件驱动架构,单台服务器能支持数万甚至数十万并发连接
  • 低资源消耗:内存占用低,CPU使用率低
  • 高扩展性:模块化设计,支持丰富的第三方模块
  • 高稳定性:长期运行不宕机,故障率低
  • 跨平台:支持Linux、Windows、macOS等多种操作系统

Nginx的用途:

  • Web服务器:托管静态网站、PHP/Python/Node.js等动态网站
  • 反向代理:将请求转发到后端服务器(如Tomcat、Node.js、PHP-FPM)
  • 负载均衡:将请求分发到多台后端服务器,提高可用性和性能
  • 缓存:缓存静态资源和动态页面,减轻后端压力
  • 安全加固:HTTPS、访问控制、限流、防DDoS等

目前,Nginx已经是全球最流行的Web服务器之一,超过Apache成为市场份额第一的Web服务器。

二、Nginx的基本配置

Nginx的配置文件通常是nginx.conf,主配置文件包含全局配置和http块,http块包含server块,server块包含location块。

一个基本的Nginx配置:

# 全局配置
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# 事件配置
events {
    worker_connections 1024;
}

# HTTP配置
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 日志格式
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    # 基本配置
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Gzip压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # 包含其他配置文件
    include /etc/nginx/conf.d/*.conf;
}

一个虚拟主机(server块)的配置:

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.html index.htm index.php;

    # 访问日志
    access_log /var/log/nginx/example.com.access.log main;
    error_log /var/log/nginx/example.com.error.log;

    # 静态文件
    location / {
        try_files $uri $uri/ =404;
    }

    # PHP处理
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

三、踩过的坑

坑1:502 Bad Gateway

这是最常见的Nginx错误之一。502表示Nginx作为反向代理,从后端服务器收到了无效的响应。

常见原因:

  1. 后端服务没启动:PHP-FPM、Tomcat、Node.js等后端服务没启动,或者端口不对
  2. 后端服务崩溃:后端服务崩溃了,无法响应请求
  3. fastcgi_pass配置错误:PHP-FPM的地址或端口配置错误
  4. 后端服务超时:后端服务处理时间太长,超过了Nginx的超时时间
  5. 后端服务返回了无效响应:后端服务返回了不符合HTTP协议的响应

我遇到的情况:有一次,配置完Nginx后,访问PHP页面一直报502。查了半天,发现是PHP-FPM没启动。启动PHP-FPM后,问题解决了。

还有一次,PHP-FPM启动了,但是还是502。查了半天,发现是fastcgi_pass配置的端口不对——PHP-FPM监听的是9000端口,但是我配置成了8000端口。改过来之后,问题解决了。

排查方法

  1. 检查后端服务是否启动:ps aux | grep php-fpmnetstat -tlnp | grep 9000
  2. 检查后端服务地址和端口是否配置正确
  3. 查看Nginx错误日志:tail -f /var/log/nginx/error.log
  4. 直接测试后端服务:curl http://127.0.0.1:9000(PHP-FPM不能直接curl,可以用cgi-fcgi测试)
  5. 检查后端服务日志:PHP-FPM日志、Tomcat日志等

解决方案

  • 启动后端服务
  • 修正配置错误
  • 增加超时时间:fastcgireadtimeout 300;proxyreadtimeout 300;
  • 优化后端服务性能,减少处理时间

坑2:404 Not Found

404表示请求的资源不存在。

常见原因:

  1. root配置错误:网站根目录配置错误,Nginx找不到文件
  2. index配置错误:默认首页配置错误
  3. 伪静态配置错误:URL重写规则错误,导致请求被转发到不存在的文件
  4. 文件确实不存在:请求的文件确实不存在
  5. 权限问题:Nginx没有权限读取文件

我遇到的情况:有一次,配置完WordPress的伪静态后,访问文章页面一直404。查了半天,发现是tryfiles配置错误——我写的是tryfiles $uri $uri/ =404;,但是WordPress需要转发到index.php,应该是try_files $uri $uri/ /index.php?$args;。改过来之后,问题解决了。

还有一次,静态文件404。查了半天,发现是root配置的路径不对——网站文件在/var/www/example.com,但是我配置成了/var/www/example。改过来之后,问题解决了。

排查方法

  1. 检查root配置是否正确
  2. 检查文件是否存在:ls -l /var/www/example.com/path/to/file
  3. 检查文件权限:Nginx运行用户是否有读权限
  4. 查看Nginx错误日志
  5. 检查伪静态/重写规则是否正确

解决方案

  • 修正root配置
  • 上传缺失的文件
  • 修改文件权限:chown -R nginx:nginx /var/www/example.comchmod -R 755 /var/www/example.com
  • 修正伪静态/重写规则

坑3:伪静态不生效

伪静态(URL重写)是Nginx配置中常见的需求,比如WordPress、ThinkPHP、Laravel等PHP框架都需要配置伪静态。

常见的伪静态配置:

# WordPress
location / {
    try_files $uri $uri/ /index.php?$args;
}

# ThinkPHP
location / {
    if (!-e $request_filename) {
        rewrite ^(.*)$ /index.php?s=$1 last;
    }
}

# Laravel
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

我遇到的情况:有一次,配置ThinkPHP的伪静态,用了ifrewrite,但是不生效。查了半天,发现是if指令在location块中的行为和预期不一样——Nginx的if是"邪恶的"(evil),有很多坑。

后来改用try_files的方式,问题解决了:

location / {
    try_files $uri $uri/ /index.php?s=$uri&$args;
}

经验:尽量用tryfiles代替if+rewritetryfiles更高效、更可靠。Nginx官方也不推荐在location中使用if

坑4:Gzip不压缩

Gzip压缩能减少传输数据量,提升网站加载速度。但是有时候配置了Gzip,却发现没有生效。

常见原因:

  1. gziptypes不包含对应的Content-Type:只配置了gzip on,但是没有配置gziptypes,或者gzip_types不包含要压缩的Content-Type
  2. 响应已经是压缩过的:后端服务已经压缩了响应,Nginx不会再压缩
  3. 响应太小:Nginx默认不压缩小于20字节的响应
  4. 客户端不支持gzip:客户端的Accept-Encoding头不包含gzip
  5. 代理场景下没配置:反向代理时,需要配置gzip_proxied any

正确的Gzip配置

gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/json
    application/xml
    application/xml+rss
    application/rss+xml
    font/ttf
    font/otf
    image/svg+xml;

排查方法

  1. curl -I -H "Accept-Encoding: gzip" http://example.com检查响应头是否有Content-Encoding: gzip
  2. 检查gzip_types是否包含对应的Content-Type
  3. 查看Nginx配置是否生效:nginx -T(打印完整配置)

坑5:HTTPS配置错误

HTTPS是现在网站的标配,但是HTTPS配置也有很多坑。

常见的HTTPS配置:

server {
    listen 443 ssl http2;
    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;
    ssl_prefer_server_ciphers on;

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    root /var/www/example.com;
    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ =404;
    }
}

# HTTP重定向到HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

常见坑

  1. 证书路径错误:证书文件路径配置错误,或者证书文件不存在
  2. 证书和密钥不匹配:证书和私钥不是一对
  3. 证书链不完整:证书文件没有包含中间证书,导致某些浏览器报不安全
  4. ssl_protocols配置错误:配置了过时的协议(如SSLv3),或者配置了不支持的协议
  5. HTTP没有重定向到HTTPS:用户访问HTTP时,没有自动跳转到HTTPS

排查方法

  1. 检查证书文件是否存在:ls -l /etc/nginx/ssl/
  2. 检查证书和密钥是否匹配:openssl x509 -noout -modulus -in example.com.crt | md5sumopenssl rsa -noout -modulus -in example.com.key | md5sum,两个MD5应该相同
  3. 用浏览器访问HTTPS,查看证书信息
  4. openssl s_client -connect example.com:443测试SSL连接

坑6:负载均衡不生效

Nginx的负载均衡配置:

upstream backend {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

常见坑

  1. 后端服务器不可达:后端服务器没启动,或者网络不通
  2. 负载均衡算法配置错误:默认是轮询(round-robin),如果配置了ip_hash,同一个IP的请求会一直发到同一台后端
  3. 健康检查没配置:Nginx默认没有主动健康检查(开源版),后端服务器挂了之后,Nginx还是会把请求发过去,导致502
  4. proxypass配置错误proxypass的地址写错了,或者没加http://

排查方法

  1. 检查后端服务器是否可达:curl http://192.168.1.10:8080
  2. 查看Nginx错误日志
  3. nginx -T检查配置是否正确
  4. 测试负载均衡是否生效:多次请求,看是否分发到不同的后端

解决方案

  • 确保后端服务器正常运行
  • 根据需求选择合适的负载均衡算法(轮询、加权轮询、iphash、leastconn等)
  • 用Nginx Plus(商业版)的主动健康检查,或者用第三方模块(如nginxupstreamcheck_module)
  • 配置被动健康检查:maxfailsfailtimeout
upstream backend {
    server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}

坑7:缓存不生效

Nginx可以缓存静态资源和反向代理的响应,减轻后端压力。

静态资源缓存配置:

location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|eot|svg)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

反向代理缓存配置:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;

server {
    location / {
        proxy_cache my_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_key $scheme$request_method$host$request_uri;
        proxy_pass http://backend;
    }
}

常见坑

  1. 缓存目录不存在或没有权限proxycachepath配置的目录不存在,或者Nginx没有写权限
  2. Set-Cookie头导致不缓存:如果后端响应有Set-Cookie头,Nginx默认不会缓存
  3. Cache-Control头导致不缓存:如果后端响应有Cache-Control: no-cache/no-store/private,Nginx不会缓存
  4. 缓存键配置错误proxycachekey配置错误,导致缓存命中率低
  5. 没有加addheader X-Cache $upstreamcache_status:无法判断缓存是否命中

排查方法

  1. 检查缓存目录是否存在、权限是否正确
  2. 查看响应头是否有Set-CookieCache-Control: no-cache
  3. addheader X-Cache $upstreamcache_status;,查看响应头的X-Cache是HIT还是MISS
  4. 查看Nginx错误日志

解决方案

  • 创建缓存目录,设置正确的权限
  • proxyignoreheaders Set-Cookie Cache-Control;忽略某些头
  • 配置合理的缓存键和缓存时间
  • X-Cache头方便调试

四、Nginx配置的最佳实践

踩了这么多坑,我总结了一些Nginx配置的最佳实践:

1. 配置文件模块化

不要把所有配置都写在nginx.conf里,应该模块化:

  • nginx.conf:全局配置
  • conf.d/*.conf:虚拟主机配置
  • snippets/*.conf:可复用的配置片段(如反向代理、HTTPS、缓存等)

include指令引入模块化的配置文件。

2. 每次修改配置后测试

每次修改Nginx配置后,一定要先测试配置是否正确:

nginx -t

如果测试通过,再重载配置:

nginx -s reload

不要直接重启Nginx,重载配置更平滑,不会中断服务。

3. 开启访问日志和错误日志

每个虚拟主机都应该配置独立的访问日志和错误日志,方便排查问题:

access_log /var/log/nginx/example.com.access.log main;
error_log /var/log/nginx/example.com.error.log;

4. 配置合理的超时时间

proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;

超时时间不要太短(后端处理慢会超时),也不要太长(后端挂了会一直等)。

5. 安全加固

  • 隐藏Nginx版本号:server_tokens off;
  • 限制请求大小:clientmaxbody_size 10m;
  • 配置HTTPS,禁用不安全的协议和加密套件
  • 配置访问控制,限制敏感路径的访问
  • 配置限流,防止DDoS攻击

6. 性能优化

  • 开启Gzip压缩
  • 配置静态资源缓存
  • 配置反向代理缓存
  • 开启sendfile、tcpnopush、tcpnodelay
  • 配置合理的workerprocesses和workerconnections

五、常见错误排查流程

最后,总结一下Nginx常见错误的排查流程:

1. 502 Bad Gateway

  1. 检查后端服务是否启动
  2. 检查后端服务地址和端口配置
  3. 查看Nginx错误日志
  4. 检查后端服务日志
  5. 测试后端服务是否正常

2. 404 Not Found

  1. 检查root配置
  2. 检查文件是否存在
  3. 检查文件权限
  4. 检查伪静态/重写规则
  5. 查看Nginx错误日志

3. 500 Internal Server Error

  1. 查看Nginx错误日志
  2. 检查后端服务日志(PHP-FPM、Tomcat等)
  3. 检查配置文件语法
  4. 检查文件权限

4. 403 Forbidden

  1. 检查文件/目录权限
  2. 检查index配置(默认首页是否存在)
  3. 检查访问控制配置(allow/deny)
  4. 查看Nginx错误日志

5. 配置不生效

  1. 测试配置:nginx -t
  2. 重载配置:nginx -s reload
  3. 查看完整配置:nginx -T
  4. 检查是否有其他配置文件覆盖了当前配置
  5. 检查是否有语法错误

六、写在最后

Nginx配置踩坑记:那些502和404的日子。

Nginx是一个强大的Web服务器,但是它的配置也有很多坑——502、404、伪静态不生效、Gzip不压缩、HTTPS配置错误、负载均衡不生效、缓存不生效……每一个坑都可能让你熬夜排查。

但是,踩坑的过程也是成长的过程。每踩一个坑,你就对Nginx的理解更深一层。现在,我再遇到Nginx配置问题,已经能比较从容地排查和解决了。

总结一下我的经验:

  1. 每次修改配置后,先nginx -t测试,再nginx -s reload重载
  2. 遇到问题先看日志——Nginx错误日志和后端服务日志,日志里通常有答案
  3. 模块化配置,不要把所有配置写在一个文件里
  4. 尽量用try_files代替if+rewrite
  5. 配置合理的超时时间、缓存、Gzip
  6. 做好安全加固和性能优化

最后,用一句话总结:Nginx配置,坑不可怕,可怕的是不看日志、不查文档、不总结。每踩一个坑,都是一次成长。

愿每一个运维和后端开发者,都能少踩Nginx的坑,多睡几个安稳觉。愿你的网站,永远没有502和404,永远稳定运行。

Nginx配置,我还在学习。这条路,没有终点。