使用njs子请求通过Location头重定向的异常问题排查
问题
需求:在使用app服务前先向其发送请求,当该请求返回状态码302时,将浏览器重定向到该请求响应中的Location地址。
配置文件 nginx.conf
http { upstream app { server localhost:3000; } server { server_name myhost.local; js_import 'lib.js'; location / { js_content lib.check; } location = /app { internal; proxy_pass http://app$request_uri; } } }
脚本文件 lib.js
async function check(r) { let reply = await r.subrequest('/app') if (reply.status == 302) { r.headersOut['Location'] = reply.headersOut['Location'] // 不生效 r.headersOut['Foo'] = 'Bar' // 正常生效 } r.headersOut['Content-Type'] = reply.headersOut['Content-Type'] // 同样正常生效 r.return(reply.status, reply.responseText) }
问题现象
执行curl -I myhost.local测试,返回的Location头被自动替换为当前请求的Host值,而非预期的目标地址:
curl -I myhost.local HTTP/1.1 302 Moved Temporarily Server: nginx/1.26.2 Location: http://myhost.local/ # Host值 Foo: Bar # 正常 Content-Type: text/html # 正常
已知reply.headersOut中存在正确的Location值,但手动设置r.headersOut['Location']无效,其他响应头正常,如何解决该问题?
解决方案
问题原因
当调用r.return(302, ...)时,Nginx核心模块会自动基于当前请求的Host生成Location头,这个过程会覆盖你之前在r.headersOut中手动设置的Location值,导致自定义地址失效。
修复方法
使用Nginx JavaScript模块提供的r.redirect()方法直接返回重定向响应,该方法会将你传入的地址作为Location头,不会触发Nginx自动生成Location的逻辑。
修改后的lib.js代码:
async function check(r) { let reply = await r.subrequest('/app') if (reply.status == 302) { // 直接用r.redirect返回重定向,传入目标Location和状态码 return r.redirect(reply.headersOut['Location'], reply.status) } // 非302状态下正常返回响应 r.headersOut['Content-Type'] = reply.headersOut['Content-Type'] r.return(reply.status, reply.responseText) }
验证
修改后重新加载Nginx配置,再次执行curl -I myhost.local,即可看到Location头被正确设置为reply.headersOut['Location']中的目标地址。
内容的提问来源于stack exchange,提问作者MapUser
相关产品推荐
相关产品推荐

