You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache 2.4负载均衡反向代理NextJS触发无限重定向故障

问题场景

我部署了2台NextJS内部应用服务器,由Apache 2.4作为反向代理对外提供服务。两台内部服务地址分别为http://internal:3000/foo和http://internal:3001/foo,对外访问地址为http://external/foo。
直接访问NextJS应用的basePath(即http://internal:3000/foo)时,服务会返回HTTP Code 308状态码重定向到http://internal:3000/foo/bar/baz,预期反向代理场景下实现相同逻辑:访问http://external/foo时自动重定向到http://external/foo/bar/baz。
对应的next.config.js配置如下:

...
module.exports = {
  ...
  async redirects() {
    return [
      {
        source: '/',
        destination: '/bar/baz',
        permanent: true
      }
    ]
  },
  basePath: 'foo'
}

未启用负载均衡、仅反向代理单台NextJS应用(例如仅代理到http://internal:3000/foo)时,上述重定向逻辑运行完全正常,使用的配置如下:

<Location "/foo">
    ProxyPass "http://localhost:3000/foo"
    ProxyPassReverse "http://localhost:3000/foo"
</Location>

配置反向代理到2台NextJS应用、启用负载均衡时,重定向逻辑完全失效,使用的配置如下:

<Proxy "balancer://example">
    BalancerMember "http://localhost:3000/foo"
    BalancerMember "http://localhost:3001/foo"
</Proxy>

<Location "/foo">
    ProxyPass "balancer://example"
    ProxyPassReverse "balancer://example"
</Location>

此时异常现象为:访问http://external/foo会持续重定向到自身,触发无限重定向循环,最终返回TOO_MANY_REDIRECT错误。单节点反向代理场景重定向正常、启用负载均衡后重定向失效的原因存疑,怀疑启用代理负载均衡时存在未感知到的响应头重写逻辑。

排查进展

进展1

排查mod_proxy_balancer.c代码,发现其中存在URL重写逻辑调用,相关代码段如下:

access_status = rewrite_url(r, *worker, url);
    /* Add the session route to request notes if present */
    if (route) {
        apr_table_setn(r->notes, "session-sticky", sticky);
        apr_table_setn(r->notes, "session-route", route);

        /* Add session info to env. */
        apr_table_setn(r->subprocess_env,
                       "BALANCER_SESSION_STICKY", sticky);
        apr_table_setn(r->subprocess_env,
                       "BALANCER_SESSION_ROUTE", route);
    }
    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01172)
                  "%s: worker (%s) rewritten to %s",
                  (*balancer)->s->name, (*worker)->s->name, *url);

    return access_status;

进展2

启用mod_dumpio模块记录Apache进出的全部流量,确认确实存在URL重写行为,具体重写流程如下:

  1. GET /foo:发送到对外Apache服务的原始请求
  2. GET /foo/:Apache转发到内部服务器的请求,路径末尾被自动追加斜杠
  3. Location /foo:内部服务器返回的重定向响应Location头,符合内部服务逻辑:内部服务收到GET /foo/请求时会重定向到Location /foo,收到GET /foo请求时才会重定向到Location /foo/bar/baz。正常场景下该重定向会由内部服务处理,即GET /foo/触发重定向到GET /foo,最终返回Location /foo/bar/baz,但反向代理场景下该流程未正常执行
  4. Location /foo:Apache对外返回的重定向响应Location头,由于该地址与步骤1的原始请求URL完全一致,因此触发了重定向循环。
根因说明

核心问题出在mod_proxy_balancer的路径规范化逻辑:

  • 当BalancerMember配置中携带路径段(比如配置的是http://localhost:3000/foo)时,模块会自动判定该路径为目录路径,转发请求时自动给末尾无斜杠的匹配路径补全斜杠,把原始请求/foo篡改为/foo/发给后端
  • NextJS对/foo和/foo/的路由处理逻辑完全不同:匹配/foo时会触发配置的根路径重定向到/bar/baz,匹配/foo/时会触发框架自带的目录规范化逻辑重定向回/foo,二者来回跳转形成无限循环
  • 单节点ProxyPass配置不会触发balancer模块的路径改写逻辑,转发路径保持原始的/foo,因此重定向逻辑正常运行
解决方案

调整Apache负载均衡配置,不要将业务路径/foo配置在BalancerMember中,仅保留后端服务的主机和端口,将路径拼接逻辑统一放到Location段的ProxyPass规则中,避免触发balancer模块的自动路径改写,修改后配置如下:

<Proxy "balancer://example">
    # 仅配置后端服务的根地址,不携带业务路径
    BalancerMember "http://localhost:3000"
    BalancerMember "http://localhost:3001"
</Proxy>

<Location "/foo">
    # 转发时统一拼接/foo路径,保证转发给后端的路径和原始请求一致
    ProxyPass "balancer://example/foo"
    ProxyPassReverse "balancer://example/foo"
</Location>

如果配置后仍存在斜杠自动补全问题,可以额外添加重写规则强制保留原始请求路径:

RewriteEngine On
RewriteRule "^/foo$" "/foo" [PT,L]

修改完成后重启Apache服务即可恢复正常重定向逻辑。

内容的提问来源于stack exchange,提问作者empiskm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 03:06:21