本地主机上的Cookies与显式域名

285

我可能对Cookie的一些基本事情有所疏忽。在本地主机上,当我在服务器端设置cookie并将域名显式指定为localhost(或.localhost)时,某些浏览器似乎无法接受该cookie。

Firefox 3.5:我在Firebug中检查了HTTP请求。我看到的是:

Set-Cookie:
    name=value;
    domain=localhost;
    expires=Thu, 16-Jul-2009 21:25:05 GMT;
    path=/
或者(当我将域名设置为.localhost时):
Set-Cookie:
    name=value;
    domain=.localhost;
    expires=Thu, 16-Jul-2009 21:25:05 GMT;
    path=/
在任何一种情况下,cookie 都不会被存储。
IE8:我没有使用任何额外的工具,但是 cookie 似乎也没有被存储,因为它没有在随后的请求中被发送回来。
Opera 9.64:localhost 和 .localhost 都可以正常工作,但是当我在首选项中检查 cookie 列表时,域名设置为 localhost.local,尽管它在 localhost 下列出(在列表分组中)。
Safari 4:localhost 和 .localhost 都可以正常工作,但是它们总是在首选项中列为 .localhost。另一方面,一个没有明确域名的 cookie,会显示为仅 localhost(没有点)。 localhost 的问题是什么?由于存在这么多不一致,必须有一些涉及 localhost 的特殊规则。而且,对我来说并不完全清楚为什么域名必须以点号开头。RFC 2109 明确规定:
“Domain 属性的值不包含任何嵌入的点或不以点开头。”
为什么?该文档指出这与安全有关。我必须承认我没有读完整个规范(可能稍后会读),但这听起来有点奇怪。基于此,在 localhost 上设置 cookie 将是不可能的。

44
六年前的帖子,但这仍然是一个问题。我正在使用Chrome v40。请参见此处 - Gaui
56
11年过去了,到了2020年,饼干问题仍然令人头疼,仍然无法解决。 - Otto
3
可能不是完全回答这个问题。在Chrome 80版本中,您可以在chrome://flags中禁用'Cookies without SameSite must be secure',以允许使用SameSite=NoneSameSite=Lax,而不仅仅是Secure。 - ntsd
25
13年过去了,到了2022年这仍然是个麻烦事。有趣的是,开发网络浏览器的公司中没有一个想到本地主机的请求不应受到cookie限制。 - user3491125
1
2022年11月21日,问题仍然存在。 - warmachine
显示剩余4条评论
25个回答

322

按照设计,域名必须至少有两个点,否则浏览器将视为无效。(请参见http://curl.haxx.se/rfc/cookie_spec.html的参考文献)

在本地工作时,cookie域必须完全省略。您不应该将其设置为""NULLFALSE,而应该设置为"localhost"。这还不够。

对于PHP,请参见http://php.net/manual/en/function.setcookie.php#73107中的注释。

如果使用Java Servlet API,请根本不要调用cookie.setDomain("...")方法。


10
我在RFC6265中没有发现有关域名中双点的内容:http://tools.ietf.org/html/rfc6265#section-5.2.3。.Net建议将其设置为“.local”,适用于本地域中的所有主机。这似乎与Opera/Safari相一致:http://msdn.microsoft.com/en-us/library/ckch3yd2.aspx。 - MandoMando
13
@Justin:嗯,你可能需要在设置 cookie 时完全省略 Domain= 参数。如果你只是将域设置为 null 或空,也许你的框架会发送带有该值的 Domain= 参数,而不是省略它?可以使用 Firebug 进行检查。 - sleske
6
这段话措辞有些不太好。"将其设置为 null、false 或空字符串" 应该改成 "根本不设置 cookie 的 'domain' 部分"。例如,使用一个简单的测试来完全省略 cookie 的 domain 部分,适用于 localhost:((domain && domain !== "localhost") ? ";domain="+domain : "") - L0j1k
如果需要两个点,127.0.0.1不会工作吗? - alec wilson
2
截至今日,这在Firefox和Chrome上都不起作用。 - user3491125
显示剩余9条评论

48

我基本上同意@Ralph Buchfelder的看法,但是以下是我的一些补充,在试图在我的本地机器(OS X / Apache / Chrome|Firefox)上复制一个具有多个子域名(例如example.com、fr.example.com、de.example.com)的系统时进行了实验。

我编辑了/etc/hosts将一些虚构的子域名指向127.0.0.1:

127.0.0.1 localexample.com
127.0.0.1 fr.localexample.com
127.0.0.1 de.localexample.com

如果我在 fr.localexample.com 上工作并且忽略了域参数,那么Cookie将正确存储在 fr.localexample.com 中,但在其他子域中不可见。

如果我使用的域名是“ .localexample.com”,则Cookie将正确存储在 fr.localexample.com 中,并且可在其他子域中看到。

如果我使用的域名是“ localexample.com”,或者我试图使用的域名只有“ localexample”或“ localhost”,则Cookie将无法存储。

如果我使用的域名是“fr.localexample.com”或“ .fr.localexample.com”,则Cookie将正确存储在 fr.localexample.com 中,而且(正确地)在其他子域中不可见。

因此,在域名中至少需要两个点的要求似乎是正确的,尽管我看不出为什么应该这样做。

如果任何人想要尝试这个,请使用下面的代码:

<html>
<head>
<title>
Testing cookies
</title>
</head>
<body>
<?php
header('HTTP/1.0 200');
$domain = 'fr.localexample.com';    // Change this to the domain you want to test.
if (!empty($_GET['v'])) {
    $val = $_GET['v'];
    print "Setting cookie to $val<br/>";
    setcookie("mycookie", $val, time() + 48 * 3600, '/', $domain);
}
print "<pre>";
print "Cookie:<br/>";
var_dump($_COOKIE);
print "Server:<br/>";
var_dump($_SERVER);
print "</pre>";
?>
</body>
</html>

45

localhost: 你可以使用:domain: ".app.localhost" ,它将起作用。设置cookie时,'domain'参数需要在域名中包含1个或多个点。然后你可以让会话跨越本地子域名工作,例如 api.app.localhost:3000


3
还在一个使用Express 3.x的node.js服务器上进行了测试并且运行良好,使用了express.session({cookie: { domain: '.app.localhost', maxAge: 24 * 60 * 60 * 1000 }}) - AmpT
5
如果您使用本地域名,应选择“THIS”作为答案!在子域名前面加一个点可以解决我的问题。 - Foxhoundn
1
那么,这个“.app.”前缀是从哪里来的呢?它是某种规范的一部分吗?对于所有不符合规范的域名(没有两个点的域名),它是否适用?此外,它能在旧浏览器中使用吗?: ^) - user2173353
1
哦...我明白了...这只是一种欺骗浏览器的技巧。好的。 - user2173353
我在这里遇到了相同的问题,但是涉及到https协议。请在此处查看:https://stackoverflow.com/questions/76148572/cross-domain-httponly-cookie-not-being-set - Mussa Charles

30

我解决了跨站点cookie问题,方法如下:

后端

服务器端

  • 服务地址:http://localhost:8080
  • 在创建响应时,设置Cookie

属性:

SameSite=None; Secure; Path=/

客户端

前端(在我这里是Angular)

  • 服务地址:http://localhost:4200/
  • 向服务器(后端)发送请求时

设置 XHR.withCredentials=true:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:8080/', true);
xhr.withCredentials = true;
xhr.send(null);

我的解释:

  • 后端和前端域不同时,决定是否将cookie 保存在前端域的cookie存储中以响应获得的决定由浏览器做出。仅当XHR请求具有withCredentials=true且正确的服务器Cookie属性(HTTP Set-Cookie头)被接收时,浏览器才允许发送cookie。

  • 后端和前端域不同时,浏览器会决定是否将cookie 发送到请求中。仅当XHR请求具有withCredentials=true时,浏览器才允许此项操作。

  • 换句话说,如果省略了withCredentials=true,则不会发送cookie也不会从响应中接收和保存cookie。

  • 所有接收到的cookie都始终存储在前端域名下的浏览器Cookie存储中。如果服务器域名不同并成功保存了cookie,则效果与第一次由前端域发送相同。

  • 如果省略SameSite=None cookie属性,则今天的浏览器(Firefox / Chrome)将使用默认的Lax模式,这对于跨站点cookie来说过于严格。

  • 如果省略了Secured cookie属性,则SameSite=None将被忽略 - 它需要设置Secured

  • 对于localhost,浏览器不要求使用HTTPS / SSL设置Secured cookie属性,http也可以工作 - 无需在https:// localhost ...下提供前端或后端。

编辑2022-03-02 - 对于Safari(v15.1),这种情况不是真的 -> 在Safari http://localhost +带有Secure的cookie中,cookie将被忽略,在浏览器中不会保存(解决方案:对于Safari + http:// localhost,如果提供则删除Secure和SameSite)。

编辑2023-01-13 - @Barnaby报告说:“Firefox拒绝设置它:'已被拒绝,因为非HTTPS cookie不能设置为“secure”。“';如果是这种情况-与Safari的解决方案应该适用(请参见上面的2022-03-02编辑)。

诊断提示:

  • 要检查是否发送了cookie-打开浏览器开发人员工具并检查网络选项卡。查找到后端的请求并检查标题-在请求标题中搜索Cookie标题,在响应头中搜索Set-Cookie
  • 要检查是否保存了cookie-打开浏览器开发人员工具,查看存储管理器(Firefox),检查Cookie并搜索前端域名,检查cookie是否存在以及如果存在,则检查它是何时创建的...
  • 不要忘记首先在后端设置CORS

参考文献:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie


1
谢谢。这非常准确! - Rice Junkie
这个答案应该排在更高的位置。我正在使用Java,我不得不像Robert说的那样精确地设置属性。你甚至不能有像Path=/api这样的东西,它必须是Path=/,所以我的整个后端cookie设置就是这样的:ResponseCookie cookie = ResponseCookie.from(jwtCookie, jwt) .path("/") .maxAge(24 * 60 * 60) .sameSite("None") .secure(true) .httpOnly(true) .build(); - JanBrus
Firefox拒绝设置它:'已被拒绝,因为非HTTPS cookie不能被设置为“安全”'。 - Barnaby
@Barnaby 谢谢,我已经在答案中添加了你的评论。 - Robert Lujo
我在这里遇到了相同的问题,但是是与https协议有关。请在这里检查:https://stackoverflow.com/questions/76148572/cross-domain-httponly-cookie-not-being-set - Mussa Charles
这一行对我起了作用:换句话说,如果省略了withCredentials=true,那么在请求中不会发送cookie,也不会接收和保存来自响应的cookie。在调用获取cookie响应的Azure函数时,我没有设置这个,只有在后续的请求中才设置了这个,认为只有在需要发送而不需要接收cookie的请求中才需要它。谢谢。 - undefined

24
当设置了一个显式域名为“localhost”的cookie时,如下所示...

Set-Cookie: name=value; domain=localhost; expires=Thu, 16-Jul-2009 21:25:05 GMT; path=/

...那么浏览器会忽略它,因为它不包含至少两个句点并且不是七个特殊处理的顶级域之一

...域名必须至少有两个(2)或三个(3)句点,以防止形式为:".com"、".edu"和"va.us"的域名。任何在下面列出的七个特殊顶级域中失败的域名只需要两个句点。任何其他域名都需要至少三个。这七个特殊的顶级域是:"COM","EDU","NET","ORG","GOV","MIL"和"INT"。

请注意,上面的句点数量可能假定需要一个前导句点。然而,现代浏览器会忽略这个句点,所以它应该写成...
至少一个或两个周期。
需要注意的是,domain属性的默认值为生成cookie响应的服务器主机名
因此,解决本地主机未设置cookie的方法是简单地不指定domain属性,并让浏览器使用默认值-这似乎没有明确值在domain属性中具有的相同约束。

我没有进行DV,但我猜其他人这样做的原因是因为你的回答并没有真正增加太多价值。关于两个句点的要求和将域属性留空的讨论已经在其他答案中提到过了。此外,你所添加的关于顶级域的内容似乎是不正确的。根据我的经验,那并不是一个必要条件。 - TTT
@TTT 不确定你是否看到了我在回答中提到的那一部分,即根据顶级域名不同,应该至少有1或2个句点,因为前导句点会被忽略?因此,我提供了一些问题背景,并补充了一个观点,我认为其他地方没有涵盖 - 显式域和浏览器默认域的规则是不同的。对我来说似乎增加了一些价值。 - Scott Munro
2
将域名设置为空(或者根本不设置)并不会导致Chrome保留针对localhost的cookie。它仍然会忽略它。请注意,这仅适用于“永久性”cookie(即设置了过期日期的cookie),因为它将继续保留针对localhost的“会话”cookie(即没有设置过期日期的cookie)。 - Triynko

13

如果您要从另一个域设置cookie(例如通过XHR跨源请求设置cookie),则需要确保在用于获取cookie的XMLHttpRequest上将withCredentials属性设置为true,如此处所述。


2
是的,即使如此,它仍然无法处理跨域请求。浏览器 - Safari、IE 11。 - Rohit Kumar
这是唯一一个帮助过我的(Chrome浏览器,客户端 - vite js,服务器端 - node + express)。 - Slava.In

5
使用PHP;对我来说,这个页面上的任何东西都没有起作用。最终我意识到在我的代码中,PHP的session_set_cookie_params()secure参数总是被设置为TRUE
由于我没有使用HTTPS访问localhost,所以我的浏览器永远不会接受这个cookie。因此,我修改了代码的这部分,根据$_SERVER['HTTP_HOST']是否为localhost来有条件地设置secure参数。现在它运行得很好。

4

不同浏览器的结果不同。

Chrome- 127.0.0.1 可以使用,但 localhost、.localhost 和 "" 不能使用。 Firefox- .localhost 可以使用,但 localhost、127.0.0.1 和 "" 不能使用。

尚未在 Opera、IE 或 Safari 中进行测试。


3
使用 Chrome V.22.0.1229.94 m 进行测试:在未提供 Domain= 参数的情况下,为 localhost 设置 cookie 是有效的。Domain= 也是有效的,但 Domain=localhost 则无效。 - sleske
Domain=localhost 在这里适用于Chrome和Firefox,只需记得在axios中设置标志 withCredentials: true 或您的http客户端js的等效标志。 - Washington Guedes

4

我在本地测试时,使用127.0.0.1作为域名的效果更好。但是我不确定为什么,使用localhost和.localhost等域名有时会出现问题。


3
唯一有效的解决方法是在cookie上设置Path=/
此外,路径属性的默认值在不同的浏览器中似乎不同,尽管我只测试了两种(Firefox和Chrome)。
Chrome会按原样设置cookie;如果在Set-Cookie头中省略path属性,则该cookie将不会被存储并被忽略。
然而,Firefox即使没有显式的path属性也会保存cookie。它只是将其设置为请求的路径; 我的请求URL是/api/v1/users,路径会自动设置为/api/v1
无论如何,当path设置为/时,两个浏览器都可以正常工作,即使没有指定域名,例如Domain=localhost或其他。因此,每个浏览器处理cookie的方式存在一些差异。

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