老用户打不开的那个页面,做SEO的工具全都返回200,因为它们不带Cookie

老用户打不开的那个页面,做SEO的工具全都返回200,因为它们不带Cookie
张文保 更新 31 分钟阅读 2,653 阅读
本文目录
  1. 为什么受伤的总是回访最多的那批人?
  2. 为什么新用户不报,老用户才报
  3. 那道墙到底卡在哪个数上?
  4. 那条指令里的两个数,只有一个管用
  5. 换成别的头,换成超长网址,结果一样吗?
  6. 现网125个站,这道墙分别修在了多高?
  7. 同一个平台,阈值出奇地整齐
  8. 为什么同一件事,服务器们给出了九种不同的答案?
  9. 431才是那个正确答案,可它是少数派
  10. 那个494是从哪儿冒出来的
  11. Google自家的文档站返回了413
  12. 最难查的是那几个报5xx的
  13. 为什么你的日志里查不到这件事?
  14. error_log里有线索,但默认级别下它不写
  15. 顺带一提:这次拒绝还会切断连接
  16. 请求经过好几层,到底是谁拒的?
  17. 边缘层还会把原因翻译成另一件事
  18. 同样的字节数,换个写法就放行了?
  19. 这件事在SEO上到底损失了什么?
  20. 你手上所有的测量工具,站的都是爬虫那一侧
  21. Cookie是从哪儿长起来的
  22. 怎么查、怎么改,一份可执行清单
  23. 第一步:十分钟确认自己站的墙在哪
  24. 第二步:把墙修高,但别指望这一步解决问题
  25. 第三步:真正该做的是给Cookie减肥
  26. 保哥拿自己的站量了一遍
  27. 常见问题解答
  28. 用户说“清一下缓存就好了”,是不是就说明是这个问题?
  29. 把large_client_header_buffers调到很大,是不是就一劳永逸了?
  30. 为什么GSC和爬虫工具一个都没报,问题却真实存在?
  31. 431和400有什么实际区别,值得为它改配置吗?
  32. 这件事会不会影响收录或者排名?
  33. HTTP/2和HTTP/1.1在这件事上表现一样吗?
  34. 权威参考资料

摘要:有一类故障,用户那边是“这网站我打不开,换个浏览器就好了”,你这边全绿。保哥拿125个真实站点挨个试:往请求里塞Cookie,一点点加,看多大会被拒。99个站测出阈值,最低6912字节,最高过了64KB也不拒;同一件事各家返回了九种状态码,规范专门设的431只占9.1%。更麻烦的是拒绝发生在应用之前——PHP一行没跑,access_log那条记录的User-Agent是个横杠,error_log默认级别一个字都不写。而Googlebot不带Cookie,你手上所有SEO工具也不带,于是这道墙谁都撞不到,只有真实老用户天天撞。

先说一个场景。客服转来一条投诉:某个客户说商品页打不开,白屏,或者一个很丑的报错。你拿链接一点,好好的。让同事试,也好好的。爬虫工具跑全站,200满屏。你回一句“没复现”,客户回一句“我换了Edge就能进了”,然后这条工单就沉了。

这类工单在保哥经手过的站里出现频率不算低,而且有个共同特征:受害者都是老客户——注册过、下过单、领过券、被埋点追踪了大半年的那批人。新用户几乎不报。

原因藏在一个很少有人量过的地方:浏览器每发一个请求,都要把这个域名下攒的所有Cookie原样带上去。攒得够多,这个请求就会大到被服务器直接扔掉,而扔掉它的那一层,在你的应用代码启动之前就已经把事办完了。

为什么受伤的总是回访最多的那批人?

Cookie有个和别的存储都不一样的性质:它不是存在浏览器里等你去读,而是每一次请求都主动跟着发出去。localStorage存5MB也不影响任何一个请求的大小,Cookie多100字节,这个域名下之后每一个请求就都多100字节。

这个“每一次”包括了页面本身、每一张图、每一个JS、每一次接口调用。所以问题不是“存了多少”,而是“发了多少遍”。

那么它能攒到多大?规范这边给的答案,比大多数人猜的要大得多。RFC 6265在谈实现限制的那一节里,对浏览器提的是最低要求:

At least 4096 bytes per cookie (as measured by the sum of the length of the cookie's name, value, and attributes). At least 50 cookies per domain. At least 3000 cookies total.

注意这三条的措辞是SHOULD provide,说的是浏览器至少要能存下这么多。每域50条、每条4096字节,乘一下是 204800字节。也就是说,一个完全守规矩的浏览器,可以合法地在一个域名下攒出200KB的Cookie,并且把它们塞进每一个请求。

而同一份文档紧接着补了一句给服务端的忠告:

Servers SHOULD use as few and as small cookies as possible to avoid reaching these implementation limits and to minimize network bandwidth due to the Cookie header being included in every request.

这句话很客气,但它暴露了一件事:规范规定了浏览器该能存多少,却从头到尾没规定服务器该能收多少。收多少是每个服务器软件、每个CDN、每个网关自己拍板的。于是这道墙的高度,全世界没有统一值。

为什么新用户不报,老用户才报

Cookie是单调增长的。第一次访问可能只有一个会话ID,逛过几个分类页、看过几个商品、关过一次弹窗、被A/B分了个桶、被三四个第三方脚本各写一条,半年下来就是几十条。而它们的过期时间大多以年计——保哥这轮抓的241条真实Cookie里,有过期时间的占77.2%,剩余天数中位数31天,但有14.5%的过期时间在一年以上。

所以这是一条时间的单行道:越忠实的用户,攒得越满,越接近那道墙。而你自己测试时用的永远是刚清过缓存的干净浏览器,或者干脆是无痕窗口。你和你的用户,从来不在同一条起跑线上。

那道墙到底卡在哪个数上?

先在自己能完全控制的环境里把机制弄清楚。保哥在一台服务器上另起了一个nginx 1.28.3实例,三个端口,唯一的区别是缓冲区配置:

端口配置说明
18745client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
出厂默认值
18746large_client_header_buffers 2 4k;收紧
18747large_client_header_buffers 8 32k;放宽

上游挂一个PHP脚本,把收到的请求头条数和字节数原样回显出来,顺便打一行 PHP_REACHED=1——这一行的作用后面会用上。

然后往Cookie里灌数据,一档一档加:

Cookie这一行的长度18745(4 8k)18746(2 4k)18747(8 32k)
3072字节200200200
4096字节200400200
8000字节200400200
8192字节400400200
24576字节400400200
32768字节400400400

二分下去,18745的精确分界是通过上界8000字节、拒绝下界8008字节。三个端口的临界值分别是8k、4k、32k,正好对上配置里 size 那个参数。

那条指令里的两个数,只有一个管用

large_client_header_buffers 4 8k 长得很像“总共给你4个8KB,一共32KB”。保哥见过不止一个人这么理解,包括很多年前的自己。nginx文档对这条指令的说明写得其实很清楚:

A request header field cannot exceed the size of one buffer as well, or the 400 (Bad Request) error is returned to the client.

关键在 a request header field——限制的对象是单个头字段,而不是所有头加起来。而Cookie恰恰是一个头字段,不管里面塞了多少个键值对,它永远只有一行。

为了确认这一点,保哥又做了一组对照:同样的总字节数,一次拆成每条20字节的小Cookie,一次拆成每条2000字节的大Cookie。

Cookie这一行的长度拆成每条20字节拆成每条2000字节
4096字节200200
8192字节400400
16384字节400400

阈值分毫不差。再把整个Cookie写成一个超长的键值对,结果还是8000通过、8192被拒。里面有多少个Cookie完全不影响判决,只有这一行有多长说了算。

所以 4 8k 里那个4,在Cookie这件事上一点用没有——它管的是“最多能有几个超大的头字段同时存在”。真正决定用户会不会撞墙的是后面那个8k。调错了参数,改完发现故障照旧,多半就栽在这儿。

换成别的头,换成超长网址,结果一样吗?

不完全一样,而且差异有意思。保哥把同样的字节量换到Referer头上:

Referer长度18745(4 8k)18747(8 32k)
2048字节200200
8192字节400200
32768字节400400

和Cookie走的是同一条规则、同一个数。这条限制对所有请求头一视同仁,Cookie只是最容易长胖的那一个。

但把长度放到网址里,返回的就不是400了:

网址长度1874518747
2048字节200200
8192字节414200
16384字节414200

414的语义是请求行太长,这也写在同一段文档里。同一个缓冲区尺寸,撑爆的位置不同,用户看到的状态码就不同——排查时如果只盯着400,遇到414会以为是另一回事。关于状态码本身怎么选、怎么影响收录,可以对照HTTP状态码这一篇里的取舍表

现网125个站,这道墙分别修在了多高?

实验台的结论是干净的,但真实世界不是一台裸nginx。前面还有CDN、WAF、负载均衡,各家都有自己的一套。保哥拿125个真实站点的首页做了一轮阶梯探测:从2KB开始往上加Cookie,找到第一个被拒的档位,再二分三次收窄。

123个站基线返回200,其中 99个测出了明确的阈值,24个站塞到64KB都照收不误

阈值上界站点数典型代表
8192字节22zappos、uniqlo、shopee
16384字节21Netlify系、AWS ELB系
32768字节24Vercel系、Cloudflare部分
40960字节5sephora、mailchimp
65536字节9Google Frontend系
其余零散档位186912到61440不等
超过64KB仍不拒24shopify、moz、MDN、rfc-editor

中位数落在16KB附近。回头看规范给浏览器的200KB合法空间:浏览器按规矩能存的量,是一半服务器能收的十几倍,而两边谁都没违规。这个缺口不是某个人配错了,是两份规范各说各的,中间没人对账。

同一个平台,阈值出奇地整齐

把阈值按Server头分组之后,出现了一个很干净的现象:

平台站点数实测阈值
Vercel9全部32768
Netlify8全部16384
AWS ELB4全部16384
Google Frontend3全部65536
Cloudflare228192到65536都有
nginx198192到65536都有

托管平台是一个值,一个不差;而Cloudflare和nginx那两行散得到处都是。这不难解释——Cloudflare只是站在前面转发,真正先喊停的是它后面那台源站。所以“我用了CDN,这事归它管”这个想法要打个折:CDN的阈值只是上限之一,真实上限取两者里更小的那个。

为什么同一件事,服务器们给出了九种不同的答案?

这是这轮普查最出乎意料的部分。99次拒绝,返回的状态码是这样分布的:

状态码次数占比它本来的意思
4005050.5%请求有语法错误(笼统兜底)
4941919.2%不在任何注册表里的自造码
43199.1%规范专为这件事设的码
41355.1%请求太大(这里请求体是空的)
直接断开连接77.1%浏览器显示“无法访问此网站”
502 / 503 / 520 / 50077.1%服务端故障
40322.0%没有权限

把这张表倒过来看更清楚:九成的情况下,用户和运维拿到的是一个不指向真实原因的信号。

431才是那个正确答案,可它是少数派

RFC 6585第5节专门为这件事分配了一个状态码:

The 431 status code indicates that the server is unwilling to process the request because its header fields are too large. The request MAY be resubmitted after reducing the size of the request header fields.

定义精准,还顺带告诉客户端该怎么自救。实测里用它的有ebay、walmart、target、paypal、netlify、render、fly.io、atlassian、bitbucket这9家。剩下九成的服务器,都在用一个更含糊的码说同一件事。

那个494是从哪儿冒出来的

19个站返回494,配上一句 494: REQUEST_HEADER_TOO_LARGE。这个码不在IANA的注册表里,是nginx内部用来标记这类错误的自定义编号——正常情况下它会在输出前被转成400,能漏到客户端说明中间某一层把它直接透传了出来。

更有意思的是这19个站的阈值:一个不差,全是32768,Server头分别是Vercel、Cloudflare、CloudFront。名字不同,底下大概率是同一套东西。

Google自家的文档站返回了413

web.dev、developers.google.com、developer.chrome.com三个站,在Cookie超限时返回的是 413 Request Entity Too Large

413说的是请求太大——而这几次请求是GET,请求体一个字节都没有。写规范、办大会、到处布道HTTP语义的那家公司,自己的文档站在这件事上答错了题。保哥看到这三行的时候笑了一下,然后把它记了下来:连他们都懒得区分,说明这个码在实践中确实没人当回事。

最难查的是那几个报5xx的

github.com是全场阈值最低的一个,6912字节就拒,而且返回的是 500 Internal Server Error,页面上写着GitHub的内部错误。运维看到500,第一反应一定是去翻应用日志、看数据库、查依赖服务——而真实原因是访客的Cookie太多了。

还有7个站干脆连响应都不给,TCP连接直接断开,浏览器显示“无法访问此网站”。这一档最狠:它长得跟断网、跟DNS故障、跟被墙一模一样。做海外站的话,这类现象会被顺手归到网络问题里去,可以对照独立站打不开的网络层排障路径逐层排除。

为什么你的日志里查不到这件事?

这才是它真正难缠的地方。保哥在实验台上发了一个40KB Cookie的请求,同时发了一个正常请求,然后去翻日志。

access_log里两行:

400 rl=53    proto=HTTP/1.1 ua=-          req="GET /x HTTP/1.1"
200 rl=79    proto=HTTP/1.1 ua=D87-Normal req="GET /x HTTP/1.1"

第一行有三个坑:

  • ua=-:User-Agent是空的。因为超长的Cookie在解析队列里排在前面,nginx读到它就停手了,后面那些头压根没被解析。这条记录里没有任何能定位到人的信息。
  • rl=53:请求长度记成了53字节。实际客户端发了4万多字节。日志里的这个数字是“成功读进缓冲区的那部分”,不是“对方发了多少”。如果你想靠请求长度筛出异常请求,恰恰筛不到出事的那些。
  • PHP没有被执行:响应体里没有那行 PHP_REACHED=1。这意味着应用日志、APM、跑在应用层的WAF、埋点统计——全都不会有这次访问的任何记录。

error_log里有线索,但默认级别下它不写

唯一一条能说清原因的记录在error_log里:

[info] client sent too long header line: "Cookie: c0=aaaa..." while reading client request headers, client: 127.0.0.1, request: "GET /x HTTP/1.1"

注意前面那个 [info]。保哥把error_log级别从info改回常见的error再跑一遍,结果是:

error_log级别这次拒绝写了几行
info1行,含完整原因
error(大多数生产配置)0行

也就是说,在绝大多数人的服务器上,这件事从头到尾不产生任何一条可读的日志。access_log里那行400没有UA、没有真实长度;error_log里什么都没有;应用侧完全不知情。这就是为什么它能在一个站上默默存在很久——日志分析那一套方法在这里整个失效,哪怕你已经把日志分析的流程跑得很熟也一样。

顺带一提:这次拒绝还会切断连接

被拒的响应长这样:

HTTP/1.1 400 Bad Request
Content-Length: 233
Connection: close

233字节的错误页,外加一个 Connection: close。正常响应是 keep-alive连接被关掉意味着这个用户后面的每一个请求都要重新建连、重新握手——如果他的Cookie已经超了,那就是每一个请求都建一次连、又被拒一次。体验上是整站瘫痪,而不是某个页面出错。

请求经过好几层,到底是谁拒的?

真实链路上,请求要过CDN、过反代、才到应用服务器,每一层的阈值都不一样。保哥搭了个两层的最小模型:前置 8 16k(较宽),后端 4 8k(较窄),然后在前置那层加一个 X-Layer 响应头做标记。

Cookie长度直连后端经过前置响应里有X-Layer吗说明
4096字节200200两层都放行
12288字节400400前置放行了,后端拒的
40960字节400400没有前置自己拒的

两次都是一模一样的400页面,用户看不出任何区别。但响应头暴露了分界线:前置层自己拒绝时,它是在nginx内部直接生成错误响应的,压根没走到配置 add_header 的那段逻辑,所以标记消失了。

这给了一条很实用的排查线索:看你在边缘层加的那些自定义响应头还在不在,就知道请求走到哪儿被拦下来的。还在,说明是后面某一层拒的;没了,说明边缘层自己就是终点。

同一次请求在两层日志里的记录也对不上:前置那层记了 rl=12622,后端那层记了 rl=78——同一个请求,两份日志差了160倍,因为两层各自只记了自己读进去的那部分。做多层架构排查时,这个数字千万别当成真实请求大小来用;日志格式里到底该记什么、CDN后面怎么拿真实信息,可以参考访问日志格式配置里的字段取舍

边缘层还会把原因翻译成另一件事

现网普查里那几个5xx就是这么来的:CloudFront后面的站被拒之后,边缘层把它翻译成了 502 ERROR: The request could not be satisfied;Cloudflare后面的翻译成了 520 Web server is returning an unknown error

这两句话都在说“我后面那台机器出问题了”,但它们后面那台机器其实什么事都没有,只是嫌请求头太长。一层一层传下来,原因就这么被洗成了另一个方向。

同样的字节数,换个写法就放行了?

实验台上的结论很干脆:只看Cookie这一行有多长,拆成几个键值对不影响。但现网有11个站不是这样。

普查时每找到一个阈值,保哥都会再补一发:同样的字节数,但整个Cookie只写成一个键值对。结果这11个站——target、klaviyo、render、atlassian、bitbucket、cloudflare、hotjar、strapi、intercom、zapier、made-in-china——拆成多条被拒,写成一条却放行了。

这说明它们判的根本不是“这一行有多长”,而是别的东西,比如Cookie的条数。具体是什么只有它们自己知道,但对排查的启发是明确的:“我把Cookie总量控制在8KB以内就安全了”这个结论,在这11个站的架构下不成立。同一个总量,条数多几条就可能翻车。

这件事在SEO上到底损失了什么?

看到这里可能会有个疑问:这不就是个用户体验问题吗,跟搜索有什么关系?

关系在于,这道墙对搜索引擎是完全隐形的。Google在讲A/B测试最佳实践的那份文档里写得很直白:

Googlebot generally doesn't support cookies. This means it will only see the content version that's accessible to users with browsers that don't accept cookies.

不带Cookie,意味着Googlebot发出的请求头永远是最短的那一种。保哥拿两种形态在实验台上量了一下:

客户端形态请求头条数请求头字节数
真实Chrome(含Cookie、Client Hints、Sec-Fetch全家)142115
Googlebot那种极简形态4240

差8.8倍。而且这240字节是不会增长的——爬虫不存Cookie,第一万次访问和第一次访问带的东西一模一样。它离8KB那道墙有34倍的余量,撞不到是必然的。

你手上所有的测量工具,站的都是爬虫那一侧

把这条链捋一遍会有点不舒服:

  • Googlebot:不带Cookie,永远200,抓取报告里干干净净
  • 各类SEO爬虫工具:默认不带Cookie,全站扫描零异常,连按UA区分客户端类型的那套办法在这里也帮不上忙
  • 正常运行时间监控:定时发一个裸请求,永远绿灯
  • PageSpeed之类的实验室测量:干净环境,测不出这件事
  • 你本人:改完配置习惯性开无痕窗口验证,同样测不到

五个测量入口,没有一个带着真实用户那身家当。而真实老用户天天在撞,撞完只会换个浏览器,不会给你发工单。Google抓取报告里那几类问题URL能查出的是另一批事,这一类天生不在它的视野里。

顺着这条思路还有一个更隐蔽的后果:如果你的站会给爬虫和用户返回不同的东西,那么爬虫拿到的那一版才是被收录的那一版,用户实际能不能打开是另一个独立事件。搜索结果里挂着一个排名不错的页面,点进去的老客户白屏——这个组合对转化的伤害,比排名掉几位要重得多。

Cookie是从哪儿长起来的

保哥用不执行JavaScript的客户端跑了一轮125个站,逛首页加三个内页,结果是中位数0字节,逛完四页的增量中位数也是0。当时差点得出“现网Cookie根本不大”的结论。

然后用真实Chrome打开同一批站里的sephora.com,读了一下 document.cookie41条、4176字节。同一个站,纯HTTP客户端只量到20条、2513字节。

document.cookie 还读不到HttpOnly那部分——真实发出去的Cookie头只会比4176更长。差距全在JavaScript上:统计脚本、广告像素、AB测试、客服挂件、同意管理平台,这些都是页面加载之后由脚本写进去的,不执行JS就一条都看不见。

这也解释了为什么第三方脚本装得越多的站越危险,以及为什么同意横幅那一套合规组件会成为一个容易被忽略的贡献者——它本身就要写好几条Cookie来记住用户的选择。

怎么查、怎么改,一份可执行清单

第一步:十分钟确认自己站的墙在哪

不需要任何工具,一条curl就够。把Cookie从4KB一路加到64KB,看哪一档开始变脸:

for n in 4096 8192 16384 32768 65536; do
  CK=$(python3 -c "print('; '.join('c%d=%s'%(i,'a'*240) for i in range($n//250)))")
  echo -n "$n -> "
  curl -s -o /dev/null -w '%{http_code}\n' -H "Cookie: $CK" https://你的域名/
done

手头没有curl的话,用在线的请求测试工具自定义一个超长Cookie头也能达到同样效果,看的就是那个状态码。

三种结果对应三种处置:

结果含义该做什么
8192就变脸裸默认配置,风险最高立刻放宽到16k以上,同时查Cookie体积
16384或32768变脸平台默认值,中等把Cookie体积压到4KB以内即可
64KB仍不拒宽松墙这边没事,但字节还在天天付

第二步:把墙修高,但别指望这一步解决问题

各家的参数不一样,常用的这几个:

软件参数出厂值
nginxlarge_client_header_buffers 的第二个数8k
ApacheLimitRequestFieldSize8190
TomcatmaxHttpHeaderSize8192
Node / Express--max-http-header-size16384

注意两点。一是反向代理链上每一层都要改,只改最外面那层没用——前面那个X-Layer实验已经演示过,改宽了前置,后端照样在原来的位置拒绝。二是放宽是有代价的:每个连接的头缓冲区变大,并发高的时候内存占用跟着涨,而且它同时也放宽了针对头部的攻击面。改到16k或32k是合理的,改到1M就是另一种事故了。多层代理各段怎么串,可以对照反向代理配置里的分段说明

第三步:真正该做的是给Cookie减肥

墙修高只是把撞墙时间往后推。按收益从高到低排:

  1. 把大对象从Cookie里搬走。购物车快照、用户偏好JSON、上次浏览的商品列表——这些应该放服务端会话或localStorage,Cookie里只留一个ID。这一条通常能砍掉一多半。
  2. 盘一遍第三方脚本各写了多少。装一个删一个的成本很低,收益却是永久的——那条Cookie之后每一个请求都不用再带。
  3. 收窄作用域。写在顶级域上的Cookie,所有子域的请求都要带,包括静态资源域和接口域。能限定到具体子域和路径的,就别写在顶级域上。作用域这件事本身还有别的连带影响,一张自家子域上的图片能把主站登录态删空那篇里有更完整的边界拆解。
  4. 给过期时间设个上限。前面那14.5%过期时间超过一年的Cookie,绝大多数并不需要活那么久。
  5. 静态资源换一个不带Cookie的域名。老办法,但在这件事上依然最有效——图片和脚本本来就不需要认识用户。

保哥拿自己的站量了一遍

顺手把本站也走了一遍流程,结果分三块:

检查项结果判断
Cookie阈值49152字节仍200,65536时连接断开墙修得很高,风险低
首访下发的Cookie一条都没有(无Set-Cookie)访客侧零负担
响应头里的Vary只有 Accept-Encoding没有多余分片

这个结果说白了是内容站的红利:没有登录、没有购物车、没挂几个第三方脚本,Cookie自然长不起来。但也正因如此,保哥这套站压根测不出这个问题——真要验证,只能自己造一个大Cookie打过去。这恰好又印证了前面那句:能不能测到,取决于你手里那个客户端像不像真实用户,而不是取决于你多认真。

常见问题解答

用户说“清一下缓存就好了”,是不是就说明是这个问题?

清缓存这个说法通常包含了清Cookie,所以它是一个有效信号,但不是充分证据。更准的判据有三条:一是换个从没访问过这个站的浏览器立刻恢复,二是无痕窗口正常、正常窗口白屏,三是整站所有页面都打不开而不是某一个页面。三条同时成立,基本可以锁定。让用户按F12打开开发者工具看一眼那个失败请求的状态码,是400、431、494里的任何一个都能坐实。

把large_client_header_buffers调到很大,是不是就一劳永逸了?

不是,有三个原因。第一,链路上每一层都有自己的限制,改了源站不改CDN等于没改,反过来也一样。第二,Cookie是单调增长的,你调到32k,用户接着攒,两年后照样撞上。第三,放宽缓冲区会同时放大内存开销和攻击面。合理做法是把墙修到16k到32k这个区间当作保险,然后把真正的力气花在压缩Cookie体积上。

为什么GSC和爬虫工具一个都没报,问题却真实存在?

因为它们发的请求里没有Cookie。Googlebot的官方说明写着它一般不支持Cookie,各类SEO爬虫默认也不带会话状态,正常运行时间监控发的更是裸请求。这道墙只对“带着一身Cookie的老用户”生效,而这类客户端在你所有的测量工具里一个都没有。要想测到,只能自己构造带大Cookie的请求主动去撞。

431和400有什么实际区别,值得为它改配置吗?

对用户体验没区别,对排查效率区别很大。431一眼就能定位到原因,400得靠猜。如果你的服务器支持自定义错误响应,把这一类错误的错误页写清楚(比如提示“请清除本站Cookie后重试”)比改状态码更实用——毕竟看到这个页面的是普通用户,他不认识431,但他认识“清除Cookie”这四个字。错误页怎么配才不踩坑,自定义错误页那一篇里有完整的配置路径。

这件事会不会影响收录或者排名?

直接影响没有——Googlebot不带Cookie,它抓到的永远是正常页面,索引和排名不会因此变化。但间接损失是实打实的:搜索结果里排在前面的那个页面,老客户点进去打不开,跳出率、转化率、复购全部受影响,而这些行为数据长期看会反过来影响表现。更麻烦的是归因——你在报表上看到的是“某批用户转化率突然下降”,很难联想到是请求头太长。

HTTP/2和HTTP/1.1在这件事上表现一样吗?

不一样,而且差得不小。实测同一台nginx、同一份配置、同一个40000字节的Cookie:HTTP/1.1返回400,HTTP/2直接放行返回200。因为 large_client_header_buffers 这条限制管的是HTTP/1.x那套解析路径,HTTP/2的头部走的是另一套机制。

这带来一个很别扭的后果:同一个用户,协议协商成HTTP/2时一切正常,某次降级回HTTP/1.1就打不开了,而降级在弱网、老旧中间设备、某些企业代理后面时有发生。用户的描述会是“有时候能开有时候不能开”,排查时通常被归到网络抖动那一栏。

权威参考资料

分享到
标签
版权声明

本文标题:《老用户打不开的那个页面,做SEO的工具全都返回200,因为它们不带Cookie》

本文链接:https://zhangwenbao.com/request-header-too-large-400-431-cookie-seo-blindspot.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交