记一次 Nginx 代理导致 API 500 错误的排查与修复
记一次 Nginx 代理导致 API 500 错误的排查与修复问题描述前端访问http://localhost:9527/dev-api/Login/getStaticResource返回 500 Internal Server Error直接访问http://anyu-portal.test/正常。技术栈前端Vue.js Vue CLI (devServer proxy)后端ThinkPHP 5.x PHP 7.4容器Docker Compose (nginx, php-fpm)代理Nginx 反向代理请求链路分析浏览器 localhost:9527/dev-api/Login/getStaticResource ↓ Vue devServer proxy (vue.config.js) Nginx anyu-portal-frontend.test/api/Login/getStaticResource ↓ Nginx location /api/ proxy_pass (anyu-front.conf) Nginx anyu-portal.test/Login/getStaticResource ↓ Nginx location ~ .php$ fastcgi_pass (anyu-portal.conf) PHP-FPM anyu-portal/Public/index.php配置文件检查1. Vue devServer 代理配置vue.config.jsproxy:{[process.env.VUE_APP_BASE_API]:{target:http://anyu-portal-frontend.test/api,changeOrigin:true,pathRewrite:{[^process.env.VUE_APP_BASE_API]:}}}VUE_APP_BASE_API/dev-api所以请求/dev-api/Login/getStaticResource被转发到http://anyu-portal-frontend.test/api/Login/getStaticResource2. Nginx 前端站点配置anyu-front.confserver { listen 80; server_name anyu-frontend.test *.anyu-frontend.test; root /www/anyu-portal-frontend; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_set_header Host $http_x_forwarded_host; proxy_pass http://anyu-portal.test/; } }3. Nginx 后端站点配置anyu-portal.confserver { listen 80; server_name anyu-portal.test; root /www/anyu-portal/Public; if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } location ~ \.php$ { fastcgi_pass php:9000; include fastcgi-php.conf; include fastcgi_params; } }4. Docker Compose 网络配置docker-compose.ymlservices:nginx:extra_hosts:-openapi:127.0.0.1-lvs:127.0.0.1-admin:127.0.0.1-portal:127.0.0.1# 缺少: anyu-portal.test:127.0.0.1排查过程步骤 1直接测试后端 API从 nginx 容器内部直接访问后端dockerexecnginxcurl-vhttp://anyu-portal.test/Login/getStaticResource结果返回 200 OK后端 API 本身正常。步骤 2测试代理链路从 nginx 容器内部通过前端域名访问dockerexecnginxcurl-vhttp://anyu-portal-frontend.test/api/Login/getStaticResource结果返回 500 Internal Server Error。步骤 3检查 Host 头问题直接访问时 curl 会发送Host: anyu-portal.test但通过/api/代理时proxy_set_header Host $http_x_forwarded_host;$http_x_forwarded_host是客户端请求的X-Forwarded-Host头通常为空。当 Host 头为空时后端 ThinkPHP 的getStaticResource方法无法正确识别SERVER_NAME导致找不到合作伙伴配置。步骤 4检查域名解析nginx 容器的extra_hosts中没有配置anyu-portal.test虽然之前已经添加了但需要确认是否生效。根因分析问题有两个层面Host 头为空proxy_set_header Host $http_x_forwarded_host;设置了一个通常为空的变量导致转发到后端时没有正确的 Host 头。域名解析nginx 容器内部无法解析anyu-portal.test虽然之前已添加到extra_hosts。后端 ThinkPHP 的getStaticResource方法依赖SERVER_NAME获取合作伙伴配置publicfunctiongetStaticResource(){$server_nameSERVER_NAME;// 依赖 $_SERVER[SERVER_NAME]$redisnewRedis();$redis-connect(C(REDIS_HOST),C(REDIS_PORT));// ...if(!C(PARTNER)){$this-setPartner($redis,$server_name);// 根据 server_name 获取合作伙伴}// ...}当 Host 头为空或不正确时SERVER_NAME无法正确识别导致C(PARTNER)为空后续操作失败。修复方案修复 1修改 Nginx 代理配置文件services/nginx/conf.d/anyu-front.conf# 修改前 location /api/ { proxy_set_header Host $http_x_forwarded_host; proxy_pass http://anyu-portal.test/; } # 修改后 location /api/ { proxy_set_header Host anyu-portal.test; proxy_pass http://anyu-portal.test/; }修复 2添加域名解析文件docker-compose.ymlservices:nginx:extra_hosts:-openapi:127.0.0.1-lvs:127.0.0.1-admin:127.0.0.1-portal:127.0.0.1-anyu-portal.test:127.0.0.1# 新增修复 3重载 Nginx 配置dockerexecnginx nginx-sreload验证dockerexecnginxcurl-vhttp://anyu-portal-frontend.test/api/Login/getStaticResource结果返回 200 OKJSON 数据正常。总结经验教训Host 头很重要反向代理时proxy_set_header Host必须设置正确的值后端框架如 ThinkPHP依赖它来识别当前站点。容器内部域名解析Docker Compose 的extra_hosts只对容器内部生效宿主机的 hosts 文件不会自动同步到容器。排查顺序先测试直接访问后端再测试完整代理链路定位问题出在哪一层。变量陷阱$http_x_forwarded_host是客户端请求头不是 Nginx 内置变量不要误以为它会自动填充。最佳实践location /api/ { proxy_set_header Host $proxy_host; # 使用 proxy_pass 中指定的主机名 proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://backend/; }使用$proxy_host可以自动获取proxy_pass中指定的主机名比硬编码更灵活。