通过代理,重写和保留URL进行Nginx重定向


71

在Nginx中,我们一直在尝试重定向URL,如下所示:

http://example.com/some/path -> http://192.168.1.24

用户仍在浏览器中看到原始URL的位置。重定向用户后,说他们单击了的链接/section/index.html,我们希望它发出一个导致重定向的请求

http://example.com/some/path/section/index.html -> http://192.168.1.24/section/index.html

并仍然保留原始网址。

我们的尝试涉及使用代理和重写规则的各种解决方案,下面显示了使我们最接近解决方案的配置(请注意,这是Web服务器的Web服务器配置example.com)。但是,这仍然存在两个问题:

  • 它无法正确执行重写,因为Web服务器收到的请求URL http://192.168.1.24包含了该URL /some/path,因此无法提供所需的页面。
  • 网页投放后,当您将鼠标悬停在链接上时,/some/pathURL丢失

    server {
        listen          80;
        server_name     www.example.com;
    
        location /some/path/ {
            proxy_pass http://192.168.1.24;
            proxy_redirect http://www.example.com/some/path http://192.168.1.24;
            proxy_set_header Host $host;
        }
    
        location / {
            index index.html;
            root  /var/www/example.com/htdocs;
        }
    }
    

我们正在寻找一种仅涉及更改上的Web服务器配置的解决方案example.com。我们可以更改192.168.1.24(也可以是Nginx)上的配置,但是我们想尝试避免这种情况,因为我们将需要对数百个通过访问进行代理的不同服务器重复此设置example.com

Answers:


59

首先,您不应该root在location块内使用指令,这是一种不好的做法。在这种情况下,这并不重要。

尝试添加第二个位置块:

location ~ /some/path/(?<section>.+)/index.html {
    proxy_pass http://192.168.1.24/$section/index.html;
    proxy_set_header Host $host;
}

这会将/ some / path /之后和index.html之前的部分捕获到$ section变量中,然后将其用于设置proxy_pass目标。如果需要,可以使正则表达式更具体。


1
后期的答复道歉-这是如此接近实现我们所期待的。唯一的缺点是,一旦提供了目标页面,浏览器中的链接URL便不会在其中包含“ / some / path /”,这意味着如果用户单击它们,它们将无法使用。如果我们能解决该问题,我将更新并接受这个答案,因为它已经存在了。
robjohncox

8
浏览器看到的链接是由在192.168.1.24服务器上运行的软件生成的。您应该修改该软件才能实现所需的功能。
Tero Kilkanen 2014年

不确定我是否遵循您关于root位置块内的警告。阅读nginx文档,这是正确的方法。他们只是警告不要在所有位置都没有默认根目录的不良做法。nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/...
家伙mograbi

好吧,有一条经验法则可以root在一个location块内不使用它,这对于默认位置不会有任何意外行为。仅当需要更改root每个位置的默认值时,才可以使用它。
Tero Kilkanen '16

1
收到$ host作为名称是什么意思?发送的确切的HTTP标头是什么,以及您要发送的确切是什么?
Tero Kilkanen '16

65

您应该在proxy_pass指令中使用URI部分。另外,您混合使用了proxy_redirect指令的顺序参数,并且可能根本不需要它。Nginx具有此指令的合理默认值。

在这种情况下,您的代码location块可能非常简单:

location /some/path/ {
    proxy_pass http://192.168.1.24/;
    # note this slash  -----------^
    proxy_set_header Host $host;
}

1
抱歉,您的回复很晚-我尝试了此操作,很遗憾,它不适用于我们的用例。问题是,当在目标服务器上发出请求时,/some/path/URL 的一部分将保留在请求中,该部分不是有效的URL(我们也需要重写URL才能删除它)。
robjohncox

@robjohncox您到底尝试了什么?
Alexey 2014年

9
斜线帮了我大忙。现在,将mydomain.com/some/path/*正确地代理到192.168.1.24/*而不是192.168.1.24/some/path/*
Vadimo 2014年

7
我可以在此回复中对“#注意此斜杠”进行投票吗?该评论三声欢呼!
8one6'1

不知道这对所有人都有效。这是我想要达到的目标。但是,当用户单击将重定向到本地服务上的例如192.168.1.24/login的链接时,他将重定向到mydomain.com/login而不是mydomain.com/some/path/login
mueslo

4

您可以使用以下配置/some/path/在前端和/后端之间实现100%无缝映射。

请注意,这是到目前为止唯一可以无缝处理产生404 Not Found错误的绝对路径的答案,前提Referer是浏览器发送了正确的HTTP 标头,因此,所有这些gif都应继续加载,而无需修改基础HTML (这不仅昂贵,而且如果没有默认情况下未编译的其他模块,则不支持)。

location /some/path/ {
    proxy_pass http://192.168.1.24/; # note the trailing slash!
}
location / {
    error_page 404 = @404;
    return 404; # this would normally be `try_files` first
}
location @404 {
    add_header Vary Referer; # sadly, no effect on 404
    if ($http_referer ~ ://[^/]*(/some/path|/the/other)/) {
        return 302 $1$uri;
    }
    return 404 "Not Found\n";
}

您可以在https://github.com/cnst/StackOverflow.cnst.nginx.conf存储库中找到完整的概念验证和最低可行产品

进行测试以确认所有边缘情况似乎都有效:

curl -v -H 'Referer: http://example.su/some/path/page.html' localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
> Referer: http://example.su/some/path/page.html
< HTTP/1.1 302 Moved Temporarily
< Location: http://localhost:6586/some/path/and/more.gif
< Vary: Referer

curl -v localhost:6586/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location
> GET /and/more.gif HTTP/1.1
< HTTP/1.1 404 Not Found

curl -v localhost:6586/some/path/and/more.gif | & fgrep -e HTTP/ -e Referer -e Location -e uri
> GET /some/path/and/more.gif HTTP/1.1
< HTTP/1.1 200 OK
request_uri:    /and/more.gif

PS:如果要映射的路径很多,那么您可能要使用基于全局的指令,而不是$http_referer在一个if内部进行正则表达式比较。location @404map

还应注意,根据相关答案proxy_pass,以及location包含在其中的尾部的斜杠都非常重要

参考文献:


2

当该斜杠添加到Nginx代理的jenkins时,将出现“看来反向代理设置已损坏”错误。

proxy_pass          http://localhost:8080/;

Remove this -----------------------------^

它应该读

proxy_pass          http://localhost:8080;

我认为这不是OP的问题所涉及或解决的任何问题。
科里·罗宾逊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.