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作为反向代理,从后端服务器收到了无效的响应。
常见原因:
- 后端服务没启动:PHP-FPM、Tomcat、Node.js等后端服务没启动,或者端口不对
- 后端服务崩溃:后端服务崩溃了,无法响应请求
- fastcgi_pass配置错误:PHP-FPM的地址或端口配置错误
- 后端服务超时:后端服务处理时间太长,超过了Nginx的超时时间
- 后端服务返回了无效响应:后端服务返回了不符合HTTP协议的响应
我遇到的情况:有一次,配置完Nginx后,访问PHP页面一直报502。查了半天,发现是PHP-FPM没启动。启动PHP-FPM后,问题解决了。
还有一次,PHP-FPM启动了,但是还是502。查了半天,发现是fastcgi_pass配置的端口不对——PHP-FPM监听的是9000端口,但是我配置成了8000端口。改过来之后,问题解决了。
排查方法:
- 检查后端服务是否启动:
ps aux | grep php-fpm、netstat -tlnp | grep 9000 - 检查后端服务地址和端口是否配置正确
- 查看Nginx错误日志:
tail -f /var/log/nginx/error.log - 直接测试后端服务:
curl http://127.0.0.1:9000(PHP-FPM不能直接curl,可以用cgi-fcgi测试) - 检查后端服务日志:PHP-FPM日志、Tomcat日志等
解决方案:
- 启动后端服务
- 修正配置错误
- 增加超时时间:
fastcgireadtimeout 300;、proxyreadtimeout 300; - 优化后端服务性能,减少处理时间
坑2:404 Not Found
404表示请求的资源不存在。
常见原因:
- root配置错误:网站根目录配置错误,Nginx找不到文件
- index配置错误:默认首页配置错误
- 伪静态配置错误:URL重写规则错误,导致请求被转发到不存在的文件
- 文件确实不存在:请求的文件确实不存在
- 权限问题: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。改过来之后,问题解决了。
排查方法:
- 检查root配置是否正确
- 检查文件是否存在:
ls -l /var/www/example.com/path/to/file - 检查文件权限:Nginx运行用户是否有读权限
- 查看Nginx错误日志
- 检查伪静态/重写规则是否正确
解决方案:
- 修正root配置
- 上传缺失的文件
- 修改文件权限:
chown -R nginx:nginx /var/www/example.com、chmod -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的伪静态,用了if和rewrite,但是不生效。查了半天,发现是if指令在location块中的行为和预期不一样——Nginx的if是"邪恶的"(evil),有很多坑。
后来改用try_files的方式,问题解决了:
location / {
try_files $uri $uri/ /index.php?s=$uri&$args;
}经验:尽量用tryfiles代替if+rewrite,tryfiles更高效、更可靠。Nginx官方也不推荐在location中使用if。
坑4:Gzip不压缩
Gzip压缩能减少传输数据量,提升网站加载速度。但是有时候配置了Gzip,却发现没有生效。
常见原因:
- gziptypes不包含对应的Content-Type:只配置了
gzip on,但是没有配置gziptypes,或者gzip_types不包含要压缩的Content-Type - 响应已经是压缩过的:后端服务已经压缩了响应,Nginx不会再压缩
- 响应太小:Nginx默认不压缩小于20字节的响应
- 客户端不支持gzip:客户端的
Accept-Encoding头不包含gzip - 代理场景下没配置:反向代理时,需要配置
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;排查方法:
- 用
curl -I -H "Accept-Encoding: gzip" http://example.com检查响应头是否有Content-Encoding: gzip - 检查
gzip_types是否包含对应的Content-Type - 查看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;
}常见坑:
- 证书路径错误:证书文件路径配置错误,或者证书文件不存在
- 证书和密钥不匹配:证书和私钥不是一对
- 证书链不完整:证书文件没有包含中间证书,导致某些浏览器报不安全
- ssl_protocols配置错误:配置了过时的协议(如SSLv3),或者配置了不支持的协议
- HTTP没有重定向到HTTPS:用户访问HTTP时,没有自动跳转到HTTPS
排查方法:
- 检查证书文件是否存在:
ls -l /etc/nginx/ssl/ - 检查证书和密钥是否匹配:
openssl x509 -noout -modulus -in example.com.crt | md5sum、openssl rsa -noout -modulus -in example.com.key | md5sum,两个MD5应该相同 - 用浏览器访问HTTPS,查看证书信息
- 用
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;
}
}常见坑:
- 后端服务器不可达:后端服务器没启动,或者网络不通
- 负载均衡算法配置错误:默认是轮询(round-robin),如果配置了
ip_hash,同一个IP的请求会一直发到同一台后端 - 健康检查没配置:Nginx默认没有主动健康检查(开源版),后端服务器挂了之后,Nginx还是会把请求发过去,导致502
- proxypass配置错误:
proxypass的地址写错了,或者没加http://
排查方法:
- 检查后端服务器是否可达:
curl http://192.168.1.10:8080 - 查看Nginx错误日志
- 用
nginx -T检查配置是否正确 - 测试负载均衡是否生效:多次请求,看是否分发到不同的后端
解决方案:
- 确保后端服务器正常运行
- 根据需求选择合适的负载均衡算法(轮询、加权轮询、iphash、leastconn等)
- 用Nginx Plus(商业版)的主动健康检查,或者用第三方模块(如nginxupstreamcheck_module)
- 配置被动健康检查:
maxfails和failtimeout
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;
}
}常见坑:
- 缓存目录不存在或没有权限:
proxycachepath配置的目录不存在,或者Nginx没有写权限 - Set-Cookie头导致不缓存:如果后端响应有
Set-Cookie头,Nginx默认不会缓存 - Cache-Control头导致不缓存:如果后端响应有
Cache-Control: no-cache/no-store/private,Nginx不会缓存 - 缓存键配置错误:
proxycachekey配置错误,导致缓存命中率低 - 没有加
addheader X-Cache $upstreamcache_status:无法判断缓存是否命中
排查方法:
- 检查缓存目录是否存在、权限是否正确
- 查看响应头是否有
Set-Cookie、Cache-Control: no-cache等 - 加
addheader X-Cache $upstreamcache_status;,查看响应头的X-Cache是HIT还是MISS - 查看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
- 检查后端服务是否启动
- 检查后端服务地址和端口配置
- 查看Nginx错误日志
- 检查后端服务日志
- 测试后端服务是否正常
2. 404 Not Found
- 检查root配置
- 检查文件是否存在
- 检查文件权限
- 检查伪静态/重写规则
- 查看Nginx错误日志
3. 500 Internal Server Error
- 查看Nginx错误日志
- 检查后端服务日志(PHP-FPM、Tomcat等)
- 检查配置文件语法
- 检查文件权限
4. 403 Forbidden
- 检查文件/目录权限
- 检查index配置(默认首页是否存在)
- 检查访问控制配置(allow/deny)
- 查看Nginx错误日志
5. 配置不生效
- 测试配置:
nginx -t - 重载配置:
nginx -s reload - 查看完整配置:
nginx -T - 检查是否有其他配置文件覆盖了当前配置
- 检查是否有语法错误
六、写在最后
Nginx配置踩坑记:那些502和404的日子。
Nginx是一个强大的Web服务器,但是它的配置也有很多坑——502、404、伪静态不生效、Gzip不压缩、HTTPS配置错误、负载均衡不生效、缓存不生效……每一个坑都可能让你熬夜排查。
但是,踩坑的过程也是成长的过程。每踩一个坑,你就对Nginx的理解更深一层。现在,我再遇到Nginx配置问题,已经能比较从容地排查和解决了。
总结一下我的经验:
- 每次修改配置后,先
nginx -t测试,再nginx -s reload重载 - 遇到问题先看日志——Nginx错误日志和后端服务日志,日志里通常有答案
- 模块化配置,不要把所有配置写在一个文件里
- 尽量用
try_files代替if+rewrite - 配置合理的超时时间、缓存、Gzip
- 做好安全加固和性能优化
最后,用一句话总结:Nginx配置,坑不可怕,可怕的是不看日志、不查文档、不总结。每踩一个坑,都是一次成长。
愿每一个运维和后端开发者,都能少踩Nginx的坑,多睡几个安稳觉。愿你的网站,永远没有502和404,永远稳定运行。
Nginx配置,我还在学习。这条路,没有终点。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录