昨天遇到一个 nginx
下非常诡异(其实也不算难,思路对了也就1秒钟的事情)的现象,花了2个小时才找到问题并解决,问题的现象还是第一次遇到,记录下,方便以后的自己和遇到类似问题的同学。
“PS:下面文章内容,是deepseek根据我们的对话记录生成的。本来想自己写的,但自己写还是太慢了点,为了积极践行 降本增效 的大方针,还是用更加高效的AI来吧
相关URL、IP等信息已经脱敏处理
一次Nginx临时目录权限引发的"诡异"路由问题排查记
“看似相同的请求为何走向不同的终点?一次权限问题引发的深度排查
问题现象:两条相似的请求,不同的目的地
在日常运维中,我遇到了一个奇怪的问题:两个具有相同路径前缀的HTTP请求,竟然被Nginx转发到了不同的上游服务器。
查看Nginx访问日志,发现了这样的记录:
192.168.1.100 - - [02/Sep/2025:17:07:45 +0800] "POST /api/service/v1/file/upload HTTP/1.0" 502 559 "https://example.com/static/webapp/v1/" "Mozilla/5.0 (Linux; Android 15; Build/AQ3A.240912.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Mobile Safari/537.36" upstream_addr=127.0.0.1:9001 upstream_status=502 upstream_response_time=0.001 request_time=0.001
192.168.1.100 - - [02/Sep/2025:17:08:32 +0800] "POST /api/service/v1/user/item/addItem HTTP/1.0" 200 95 "https://example.com/static/webapp/v1/" "Mozilla/5.0 (Linux; Android 15; Build/AQ3A.240912.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Mobile Safari/537.36" upstream_addr=192.168.10.20:8080 upstream_status=200 upstream_response_time=0.124 request_time=0.125
从日志可以看出,两个请求都有相同的路径前缀/api/service/v1/
,但第一个请求被转发到了127.0.0.1:9001
并返回502错误,第二个请求则被正确转发到了192.168.10.20:8080
并返回200成功。
排查过程:一步步揭开迷雾
第一阶段:增强日志,确认问题
首先,我们在Nginx配置中增加了详细的日志格式,希望能够捕获更多信息:
log_format detailed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'upstream_addr=$upstream_addr upstream_status=$upstream_status '
'uri=$uri request_uri=$request_uri';
access_log /var/log/nginx/detailed_access.log detailed;
启用详细日志后,我们确认了两个具有相同前缀的请求确实被路由到了不同的upstream,这与预期行为不符。
第二阶段:排查location配置
我们检查了Nginx配置,发现有两个location块:
location /api/service/v1/ {
proxy_pass http://192.168.10.20:8080/api/;
}
location / {
proxy_pass http://127.0.0.1:9001/;
}
按照Nginx的最长前缀匹配原则,两个请求都应该匹配到第一个location块。但实际情况并非如此。
为了排除默认location的干扰,我们尝试注释掉了第二个location块:
# location / {
# proxy_pass http://127.0.0.1:9001/;
# }
重启Nginx后,惊讶地发现被错误转发请求的upstream_addr变成了空,这表明请求根本没有被正确代理到任何上游服务器。
第三阶段:发现关键错误日志
在前两个阶段都没有找到根本原因后,我们几乎是无意中查看了Nginx的错误日志( error.log ) ,这才发现了关键线索:
2025/09/03 07:58:41 [crit] 1340742#0: *277929 open() "/usr/local/nginx/client_body_temp/0000000054" failed (13: Permission denied), client: 192.168.1.100, server: example.com, request: "POST /api/service/v1/file/upload HTTP/1.0", host: "api.example.com", referrer: "https://example.com/static/webapp/v1/"
这个错误信息很关键——Nginx进程没有权限在临时目录中创建文件。
根源分析:临时目录与Nginx的fallback机制
1. Nginx如何处理大请求体
当Nginx处理客户端请求时,如果请求体较小,会使用内存缓冲区直接处理。但当请求体较大(超过client_body_buffer_size
设置)时,Nginx会将请求体写入临时文件中,以避免占用过多内存。
2. 权限失败引发的连锁反应
当第一个请求(文件上传)到达时:
- 但Nginx worker进程没有临时目录的写权限
- 触发了Nginx的错误处理机制,将请求fallback到默认location
而第二个请求(普通API调用):
解决方案:创建有权限的临时目录
通过以下步骤解决了这个问题:
1. 创建有权限的临时目录
mkdir -p /tmp/nginx_temp/client_body_temp
2. 在nginx.conf中配置临时目录路径
在http块中添加配置:
http {
# 其他配置...
# 指定临时目录到有权限的位置
client_body_temp_path /tmp/nginx_temp/client_body_temp;
# 其他配置...
}
3. 确保Nginx进程有读写权限
chown -R nginx:nginx /tmp/nginx_temp/client_body_temp
chmod -R 755 /tmp/nginx_temp/client_body_temp
4. 重新加载Nginx配置
nginx -s reload
经验总结与反思
这次排查经历给了我们几个重要启示:
- 日志是关键:没有详细的访问日志和错误日志,很难快速定位问题
- 排查要有系统性:从表象到本质,从access.log到error.log,需要有条不紊地进行
- 权限问题很隐蔽:Nginx的权限问题往往表现为其他症状,需要深入挖掘
- 默认配置可能不适用:生产环境中需要明确指定各种路径和权限,不能依赖默认值
排查过程中的关键步骤:
- 通过修改配置(注释默认location)缩小问题范围
预防措施与最佳实践
为了避免类似问题,建议采取以下措施:
# 示例:完整的客户端请求体配置
http {
client_max_body_size 100M;
client_body_buffer_size 16k;
client_body_temp_path /var/tmp/nginx_client_temp;
client_body_timeout 60s;
# 确保目录存在且有权限
# mkdir -p /var/tmp/nginx_client_temp
# chown -R nginx:nginx /var/tmp/nginx_client_temp
# chmod -R 755 /var/tmp/nginx_client_temp
}
亲爱的读者朋友们,欢迎在评论区分享你的经验和见解!
🎯 讨论话题:
- 你在使用Nginx过程中遇到过哪些"诡异"的问题?最后是如何解决的?
- 关于Nginx的权限管理,你有哪些最佳实践想要分享?
- 本文的排查思路对你是否有启发?你会如何避免类似的坑?
💡 如果你觉得这篇文章对你有帮助:
🔍 延伸思考:
- 除了临时目录权限,还有哪些看似小却可能引发大问题的Nginx配置细节?
- 在你的生产环境中,是如何监控和预防这类权限问题的?
阅读原文:原文链接
该文章在 2025/9/8 9:33:29 编辑过