URL 片段和 302 重定向

154

众所周知,URL片段(#后面的部分)不会被发送到服务器。

但我想知道,当涉及到服务器重定向(通过HTTP状态302和 Location: 头部)时,片段如何工作。

我的问题实际上是双重的:

  1. 如果原始URL有一个片段(/original.php#foo),并且重定向到/new.php,原始URL的片段部分是否会丢失?或者它有时会应用于新的URL吗?在这种情况下,新的URL是否可能是/new.php#foo

  2. 无论原始URL如何,如果服务器重定向到具有片段的新URL(/new.php#foo),片段是否会被"尊重"? 或者服务器真的没有干涉片段的业务 - 浏览器是否会忽略它,直接转到/new.php


1
在这里,您可以找到W3C的规范:http://www.w3.org/TR/cuap#uri第4.1条款。在重定向时应保留片段。 - Marcin
1
@Marcin:W3C TAG提出了不同的建议:http://lists.w3.org/Archives/Public/ietf-http-wg/2010OctDec/0504.html。相关问题:[302重定向到相对URL是有效还是无效?](https://dev59.com/3msy5IYBdhLWcg3w4x_c) - hakre
4个回答

150
2022-09-22更新: RFC 9110/STD 97 HTTP语义(取代7231等其他标准)已于2022年6月发布为INTERNET标准。新编号的第10.2.2节Location的措辞与之前/以下相同。 2014-Jun-27更新: RFC 7231,超文本传输协议(HTTP / 1.1):语义和内容,已作为建议标准发布。从更改日志中可以看出:
引用:

Location头字段的语法已更改以允许所有URI引用,包括相对引用和片段,在何时使用片段不适当方面进行了一些澄清。(第7.1.2节)

第7.1.2节Location中的重要点:
引用:

如果3xx(重定向)响应中提供的Location值没有片段组件,则用户代理必须将重定向的处理视为该值继承用于生成请求目标的URI引用的片段组件(即,如果有原始引用的片段,则重定向会继承原始引用的片段)。

引用:

例如,为URI参考“http://www.example.org/~tim”生成的GET请求可能导致包含标题字段的303(See Other)响应:

Location: /People.html#tim

这说明用户代理应该重定向到“http://www.example.org/People.html#tim”。

同样,对于URI引用“http://www.example.org/index.html#larry”生成的GET请求可能会导致301(永久移动)响应,其中包含头字段:

Location: http://www.example.net/index.html

这表明用户代理应重定向到“http://www.example.net/index.html#larry”,保留原始片段标识符。

这应该清楚地回答了你的问题。

更新结束

这是当前HTTP规范中未指定的问题。在IETF httpbis工作组的2个问题中进行了解决:

#6 允许在 Location 标头中使用片段标识符。 #43说:

我用各种浏览器进行了测试。

  • Firefox和Safari使用位置标头中的片段标识符
  • Opera使用源URI中的片段(如果存在),否则使用重定向位置的片段
  • IE(8)忽略位置URI中的片段标识符,因此将使用源URI中的片段标识符(如果存在)

提案:

“注意:当需要组合原始URI和重定向的片段标识符时,其行为未定义;当前的用户代理确实不同意哪个片段具有优先权。”

[...]

看起来IE8确实使用了Location的片段标识符(我所看到的行为可能仅限于本地主机)。

因此我们似乎对Safari / IE / Firefox / Chrome(刚测试)具有一致的行为,即使用Location标头中的片段标识符,无论原始URI是什么。

因此,我更改我的建议以记录该作为预期行为。

这导致了最兼容浏览器和未来证明正确(因为这个问题最终会被标准化)的回答:

A: 原始URL的片段标识符被丢弃。

B: 使用 Location 标头的片段标识符。


1
我已经忘记了在HTTP服务器中设置的一些“重写”规则,这些规则可能是作为301重定向实现的。因此,IE一直失去片段标识符,因为当您有多个重定向时,第一个重定向设置的片段成为第二个重定向中 URI的一部分。 - Eugene Yokota
Opera 12.12在Location头中存在Fragment时会予以尊重。 - goat
4
在当前版本的Chrome和Firefox中,语句A不成立。在当前版本的Firefox中,语句B也不成立。目前来看,如果需要使用哈希(例如使用Backbone的路由),似乎基于JavaScript的重定向是您唯一真正的选择。 - a.real.human.being
引用的代码块似乎自相矛盾。首先它说“IE(8)忽略位置URI中的片段,因此在源URI中存在时将使用该片段”,然后稍后又说“似乎IE8确实使用来自位置的片段标识符”。第一个是指与第二个不同的内容吗? - davidtbernal
Chome 45.0.2454.85中B不为真。Firefox 40.0.3中B为真。 - Jingguo Yao

48

Safari 5 和 IE9 及以下版本在发生 HTTP/3xx 重定向时会删除原始 URI 的片段。如果响应的 Location 标头指定了片段,则使用该片段。

IE10+、Chrome 11+、Firefox 4+ 和 Opera 在遵循 3xx 重定向后都会“重新附加”原始 URI 的片段。

测试页面:http://www.webdbg.com/test/redir/fragment/

有关此问题的进一步讨论,请参见此处


2
实际上,IE10仍然与最新的Firefox和Chrome版本有所不同。在简单重定向的情况下,它似乎会保留源URL中的片段。如果重定向的“位置”包含一个片段,它将正确地保留它。但是,如果带有片段的重定向“位置”经过另一个3xx重定向,它将莫名其妙地忽略第一次重定向的片段,这与前两个行为不一致。Chrome和Firefox始终保留它。 - odony
我已确认你是正确的。请查看此页面上的最终测试链接:http://www.webdbg.com/test/redir/fragment/ - EricLaw
删除重定向链中任何先前URL的#片段是有道理的。您很可能访问一个不再适用/存在该锚点的页面。 - Walt Howard

12

提醒一下,您可以在w3c中找到适当的规范,定义了所有内容应如何运作:http://www.w3.org/TR/cuap#uri - 第4.1条 - 请参见下文:

当资源(URI1)移动时,HTTP重定向可以指示其新位置(URI2)。

如果URI1具有片段标识符#frag,则用户代理程序应尝试到达的新目标将是URI2#frag。如果URI2已经具有片段标识符,则不得附加#frag,并且新目标是URI2。

错误:大多数当前的用户代理实现了HTTP重定向,但不会将片段标识符附加到新的URI中,这通常会让用户感到困惑,因为他们会得到错误的资源。

参考资料:

HTTP重定向在HTTP/1.1规范[RFC2616]的第10.3节中有描述。详细的行为要求在“处理重定向URL中的片段标识符”[RURL]中有描述。术语“持久统一资源定位符(PURL)”指代一个通过HTTP重定向指向另一个URL(URI的特殊情况)。有关更多信息,请参阅“持久统一资源定位符”[PURL]。示例:

假设用户请求http://www.w3.org/TR/WD-ruby/#changes的资源,而服务器将用户代理重定向到http://www.w3.org/TR/ruby/。在获取后者URI之前,浏览器应将片段标识符#changes附加到它:http://www.w3.org/TR/ruby/#changes


0
发布类似于我遇到的具有解决方案的问题
希望这对于有着类似在IE中保留哈希值以进行302重定向的需求的人有所帮助。

添加答案必要部分而不仅仅是链接

我们在应用程序中使用SiteMinder身份验证。
我发现,在成功验证后,SiteMinder使用类似于名称为redirect登录表单隐藏变量value(其中它存储用户请求的URL/myapp/ - 没有哈希片段,因为它不会被发送到服务器)进行302重定向到用户请求的应用程序页面。下面是示例表单:

Sample login form

由于redirect隐藏变量的仅包含没有哈希片段的/myapp/,并且它是一个302重定向,因此即使在我们的应用程序中尝试的任何解决方案都不起作用,IE也会自动删除哈希片段。

IE只会重定向到/myapp/,并且会着陆在我们应用程序的默认主页https://ourapp.com/myapp/#/home上。

浪费了将近一天的时间来弄清楚这种行为。

解决方案是:

已更改登录表单隐藏变量(redirect以保留哈希片段,方法是将window.location.hash附加到现有值中。类似下面的代码

$(function () {
  var $redirect = $('input[name="redirect"]');
  $redirect.val($redirect.val() + window.location.hash);
});

在这个更改之后,redirect 隐藏变量将用户请求的 URL 值存储为 /myapp/#/pending/requests,并且 SiteMinder 在 IE 中将其重定向到 /myapp/#/pending/requests

以上解决方案在三个浏览器 Chrome、Firefox 和 IE 中都能正常工作。

感谢 @AlexFord 提供了详细说明和解决方案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接