Nginx 反向代理防止更改其他位置的主机

Nginx reverse proxy prevent changing host for other locations

我花了几天时间尝试完成这件事,但我做不到。我有两个 Web 应用程序,一个是生产应用程序,一个是测试应用程序,假设 https://production.apphttps://testing.app。我需要在 https://production.app/location 下创建一个指向 https://testing.app/location 的反向代理(目前我只需要产品中测试环境的一项功能)。我创建的配置确实代理了这个确切位置,但该功能还从 /static 目录加载资源,导致请求 https://production.app/static/xyz.js 而不是 https://testing.app/static/xyz.js,并且 /static 不能代理。有没有办法仅在这个代理流量中更改 headers,使其成为 https://testing.app/static(当然还有任何其他位置)?

以下是我当前的配置(仅关于代理的指令):

server {
    listen 443 ssl;
    server_name production.app;
    root /var/www/production.app;

    location /location/ {
        proxy_pass  https://testing.app/location/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

祝你有美好的一天:)

你的问题标题写得不好。它与“更改 headers” 无关,nginx 位置虽然本质上与请求处理相关,但也不是正确的术语。它应该类似于“如何以与其他方式不同的方式提供代理页面请求的资产”。

好的,我们开始吧。首先,你真的了解下面的配置到底是做什么的吗?

location /location/ {
    proxy_pass https://testing.app/location/;
}

https://testing.app 上游名称之后指定的 URI 前缀 /location/ 具有以下效果:

  1. location 指令中指定的 /location/ 前缀从已处理的 URI 中截断。
  2. 已处理的 UIR 在被传递到上游之前被添加了 proxy_pass 指令中指定的 /location/ 前缀。

也就是说,如果这些前缀相等,则以下配置将执行相同的操作以消除这些步骤(因此处理请求的效率会稍微高一些):

location /location/ {
    proxy_pass https://testing.app;
}

了解这是我们将要使用的技巧的关键概念。

因为从 /location/ 页面发出的所有静态资产请求都将具有 HTTP Referer header equal to http://production.app/location/ (well, unless you specify no-referer as you page referrer policy,如果你是,除非你改变它,否则整个技巧将根本不起作用) ,我们可以重写一个符合某些条件的请求 URI(比如以 /static//img/ 前缀开头),使用下面的 if 块(应该放在server 配置级别):

if ($http_referer = https://production.app/location/) {
    rewrite ^/static/ /internal$uri;
    rewrite ^/img/ /internal$uri;
    ...
}

我们可以使用任何前缀,这里使用的 /internal 仅作为示例,但是使用的前缀不应干扰您现有的路由。接下来,我们将使用 internal 关键字定义该特殊位置:

location /internal/ {
    internal;
    proxy_pass https://testing.app/;
}

此处 http://testing.app 上游名称后的尾部斜线是必不可少的部分。它将使代理的 URI 返回到其原始状态,删除之前 rewrite 指令添加的 /internal/ 前缀,并将其替换为单个斜杠。

您可以将此技巧用于多个页面,使用正则表达式模式匹配 Referer header 值,例如

if ($http_referer ~ ^https?://production\.app/(page1|page2|page3)/) {
    ...
}

除静态资产外,您不应该尝试此操作,否则它会破坏应用程序路由机制。这也只是一种仅应用于测试目的的解决方法,我不建议长期用于生产。


顺便说一句,你确定你真的需要那个吗

proxy_set_header Host $host;

在你所在的位置?你真的明白它的意思吗,或者你只是从其他配置中使用它 copy-pasting ?检查 this 答案,如果出现问题,请不要感到惊讶。