同一个页面11种URL写法都返回200,做SEO查重复内容时一条都不会报

同一个页面11种URL写法都返回200,做SEO查重复内容时一条都不会报
张文保 35 分钟阅读 4,778 阅读
本文目录
  1. 一个页面到底有几个地址?先在实验台上数一遍
  2. 十一种写法,返回的字节数一个不差
  3. 被挡住的是哪些
  4. nginx为什么把这么多写法都认成同一个页面?
  5. 这三步都不是可选项
  6. 归一化跑在你的配置之前
  7. 大小写是唯一一条真分界线吗?
  8. 可现实里三成多的站不是这样
  9. 拿本站当对照
  10. 关掉merge_slashes是在收紧还是在放松?
  11. 真正变化的是别的东西
  12. 现网105个站,一个页面平均有几个能返回200的地址?
  13. 怎么测的
  14. 结果
  15. 最松的和最严的
  16. canonical到底收不收得住这些变体?
  17. 为什么它这么稳
  18. 那还有什么可担心的
  19. 哪些写法是你自己配出来的?
  20. 那条大小写不敏感的location
  21. 兜底路由
  22. 还有一个挡住了
  23. 该怎么把一个页面的地址收敛回一个?
  24. 能直接抄的收敛配置
  25. 三条命令自查
  26. 什么时候不值得治
  27. 常见问题解答
  28. 同一个页面有多个能返回200的地址,会被谷歌当成重复内容惩罚吗?
  29. 我加了canonical,是不是就不用管URL变体了?
  30. 路径里的大小写到底要不要统一成小写?
  31. 把merge_slashes关掉能不能挡住多余斜杠的地址?
  32. 为什么我在nginx里写规则去跳转带双斜杠的地址,规则从来不生效?
  33. 兜底路由(try_files最后指向应用)是不是必须改掉?
  34. 这些多出来的地址会不会稀释页面权重?
  35. 权威参考资料

摘要:在一台干净的nginx上放一个165字节的文件,然后换着花样去请求它——加一道斜杠、加一个点、把斜杠写成 %2F、在中间塞一段 zz/..。十一种写法全部返回200,返回的字节数一个不差,而nginx内部记录的路径永远是同一个。这不是配置失误,是HTTP服务器按规范必须做的归一化,它发生在你写的每一条location生效之前。真正被挡住的只有大小写。往外看105个真实站点,一个页面平均带着2.66个字节级完全相同的200地址,六成以上的站至少多出一个。好消息是canonical在这批变体上一次都没掉链子;坏消息是有17.1% 的页面压根没有canonical。

先说清楚这篇文章要数的是什么:不是“URL该怎么起名”,也不是“重复内容会不会被罚”,而是一个更基础、却几乎没人真去数过的问题——同一份内容,到底有多少种地址写法能让服务器痛痛快快回一个200?

这个数字很重要,因为搜索引擎眼里的“页面”就是URL。你觉得自己发布了一个页面,服务器可能同时承认了十一个。

一个页面到底有几个地址?先在实验台上数一遍

要数清楚,得排除干扰。生产站上跑着CDN、WAF、伪静态、缓存,任何一层都可能替你把地址改掉,结论就不干净了。所以我在服务器上另起了一个独立的nginx实例,不碰生产:自己的配置文件、自己的日志目录、端口18545,root指向一个只有几个文件的目录。

里面放一个真实存在的文件 /foo/bar.html,165字节。然后我给每个server加了一行:

add_header X-Nginx-Uri "$uri" always;
add_header X-Req-Uri   "$request_uri" always;

$request_uri 是客户端原样发过来的请求行,$uri 是nginx在内部实际用来找文件、匹配location的那个路径。把两个都打出来,就能看见nginx到底在哪一步把地址改了。

接下来用curl换着花样请求同一个文件。这里有个细节必须交代:curl默认会替你把 ./../ 先化简掉,那样测的就是curl而不是nginx了。加上 --path-as-is 才能把原始字符串原样送出去。

十一种写法,返回的字节数一个不差

请求行原样状态码返回字节nginx内部 $uri
/foo/bar.html200165/foo/bar.html
//foo/bar.html200165/foo/bar.html
/foo//bar.html200165/foo/bar.html
///foo///bar.html200165/foo/bar.html
/foo/./bar.html200165/foo/bar.html
/./foo/bar.html200165/foo/bar.html
/foo/xx/../bar.html200165/foo/bar.html
/foo/../foo/bar.html200165/foo/bar.html
/foo%2Fbar.html200165/foo/bar.html
/%66oo/bar.html200165/foo/bar.html
/foo/bar%2Ehtml200165/foo/bar.html

十一行,状态码全是200,字节数全是165,$uri 那一列从头到尾没变过。

最后一列是关键。nginx不是“分别处理了十一个地址并且碰巧都返回了同样的内容”,而是它压根就没看见这十一个地址——它只看见一个。那十一种写法的差别,在请求进入你的配置之前就已经被抹平了。

顺带一提,第九行的 %2F 是斜杠的百分号编码。有人专门用它来表示“这是一个字面上的斜杠,不是路径分隔符”,但在这里它被解码成了真斜杠,然后当成路径分隔符用掉了。关于百分号编码在URL各个位置的不同含义,可以看这篇URI编解码器怎么用?中文URL编码、UTM参数转码与GSC乱码网址还原

被挡住的是哪些

同一台机器上,下面这些写法拿到的是404:

请求行原样状态码nginx内部 $uri为什么
/FOO/BAR.HTML404/FOO/BAR.HTML路径大小写没有被归一化
/Foo/bar.html404/Foo/bar.html同上,一个字母也不行
/foo/bar.html.404/foo/bar.html.尾部的点不会被吃掉
/foo/bar.html;a=1404/foo/bar.html;a=1分号参数是路径的一部分
/foo/bar.html%20404/foo/bar.html␣解码出一个真空格,文件名不匹配
/foo/bar.html/404/foo/bar.html/文件不是目录

还有一个例外值得单独记一笔:/foo/bar.html?x=1 返回200。查询串不参与路径匹配,所以你可以在任何一个页面后面挂上任意查询串,服务器照样给你200,内容一字不差。这条对搜索引擎的意义完全不同于路径变体——它是分面导航和追踪参数把URL数量炸开的根源,展开讲是另一篇的量,分面导航的SEO怎么治理?筛选过滤产生的海量URL别拖垮抓取预算里拆得比较细。

nginx为什么把这么多写法都认成同一个页面?

这不是nginx的自作主张,官方文档在location那一节写得很直白:

The matching is performed against a normalized URI, after decoding the text encoded in the "%XX" form, resolving references to relative path components "." and "..", and possible compression of two or more adjacent slashes into a single slash.

一句话三个动作,顺序还是固定的:

  1. 先解码:把 %XX 形式的字符还原成本来的样子。%66 变成 f%2F 变成 /%2E 变成 .
  2. 再解析点段:把 ... 这两种相对路径引用算掉。
  3. 最后压缩斜杠:连续两个以上的斜杠合并成一个。

为什么是这个顺序,实验台上能看出来。/foo/bar%2Ehtml 里的 %2E 先被还原成点,然后才轮到点段解析——但这个点是文件名里的点,不构成 ... 这样的完整路径段,所以留了下来。而 /foo%2Fbar.html 里的 %2F 被还原成斜杠之后,它就是一个货真价实的路径分隔符了,再想让它变回“字面上的斜杠”已经没有机会。

这三步都不是可选项

点段那一步的出处在RFC 3986。规范用了一整节讲 remove_dot_segments 算法,并且在讲URI归一化的时候明确要求:

URI normalizers should remove dot-segments by applying the remove_dot_segments algorithm to the path.

换句话说,任何一个正经实现都得这么做。这一条不是“nginx的脾气”,是所有人的共同底线。

这句话不用只当规范来信,后面那轮普查里正好有现成的对照:Apache官方自己的站 httpd.apache.org(响应头 Server: Apache),在点段、父段、开头双斜杠、路径内双斜杠这四种写法上全部原地返回200,且字节与规范地址完全相同。换个服务器实现,答案一模一样。

归一化跑在你的配置之前

这才是最要命的一点,也是我认为最值得单独拎出来的一句:

你在nginx里写的每一条location、每一个if、每一段rewrite,看到的都已经是归一化之后的路径。原始写法在到达它们之前就没了。

所以那种“我加一条规则把带双斜杠的请求301到规范地址”的想法,默认配置下根本没法实现——等你的规则拿到手时,双斜杠已经不在了。这跟 Nginx配置每次reload都通过,Googlebot每8次抓取却有1次撞在301上里讲的那类“配置通过了但行为不是你想的那样”是同一类问题:nginx -t 只检查语法,不检查你对执行顺序的理解。

想拿到原始写法,只有 $request_uri 这一个口子。这也是为什么本文的实验台要把两个变量都打出来——只看 $uri,你会以为世界很干净。

大小写是唯一一条真分界线吗?

实验台上,大小写是唯一一个把请求挡在门外的因素。这背后也有规范依据,RFC 3986讲得很细:

the scheme and host are case-insensitive and therefore should be normalized to lowercase... The other generic syntax components are assumed to be case-sensitive unless specifically defined otherwise by the scheme.

协议名和主机名不区分大小写,路径、查询串、片段这些“其他部分”默认区分。所以 https://EXAMPLE.com/Pagehttps://example.com/Page 是同一个地址,而 /Page/page 不是。

谷歌的表态和规范完全一致,而且直接点了名:

Like any other HTTP client following IETF STD 66, Google Search's URL handling is case sensitive (for example, Google treats both /APPLE and /apple as distinct URLs with their own content).

这句话里的IETF STD 66就是RFC 3986。谷歌明说自己按这份规范处理URL,把 /APPLE/apple 当两个各有各内容的地址。

可现实里三成多的站不是这样

规范归规范,服务器给不给你面子是另一回事。我拿105个真实站点的深层页面做了大小写探测,把路径整个转成大写再请求一次,结果分成三档:

路径全大写之后站数占比意味着什么
4046763.8%大小写敏感,行为符合规范
3xx跳回原地址后2002321.9%做了归一化,主动收敛,最省心
原地直接2001413.3%两个大小写各是一个可索引地址
其他(4xx非404)11.0%被防护层拦下

最后那13.3% 是要出事的那一档。服务器认了大写地址、原地返回200,而谷歌按规范把它当成另一个页面。一个页面于是有了两个可索引地址,内容一模一样。

这一档通常来自四个地方:跑在Windows/IIS上(NTFS默认不区分大小写)、跑在macOS开发机同款文件系统上、前面挂了会做小写归一化的CDN规则、或者应用层用了不区分大小写的路由匹配(下一节会专门拆这个)。

谷歌给的建议也很实在:

If upper and lower case text in a URL is treated the same by your web server, convert all text to the same case so it's easier for Google to determine that URLs reference the same page.

注意它的前提——“如果你的服务器把大小写当成同一回事”。它没让所有人都去做小写归一化,只让那13.3% 去做。剩下63.8% 本来就是404,做了反而多此一举。

拿本站当对照

顺手拿保哥自己这个站测了一遍同一批写法,结果和实验台的出厂默认差得挺远:

请求状态码Location
/glossary/200
/glossary301/glossary/
/GLOSSARY/301/glossary/
//glossary/301/glossary/
/glossary//404
/./glossary/404
/x/../glossary/404

同样是nginx,出厂默认能进十一个门,这边只剩两个:规范地址本身,加上三种会被301送回规范地址的写法。差别全在配置上。

关掉merge_slashes是在收紧还是在放松?

知道斜杠会被合并之后,很多人的第一反应是把它关掉——既然多余斜杠是“脏”的,那就别让它进来。

我在实验台上开了第二个server,唯一的差别是加了一行 merge_slashes off;。测下来是这样:

请求行默认(on)状态码默认 $urioff状态码off的 $uri
//foo/bar.html200/foo/bar.html200//foo/bar.html
/foo//bar.html200/foo/bar.html200/foo//bar.html
/foo/./bar.html200/foo/bar.html200/foo/bar.html
/foo/xx/../bar.html200/foo/bar.html200/foo/bar.html

两个关键结论:

第一,关掉之后照样返回200。因为最后去磁盘上取文件的是操作系统,而Linux把路径里的连续斜杠视同一个。nginx不再帮你合并,内核帮你合并。你以为堵住了门,其实只是把门牌换了个写法。

第二,点段照样被算掉。merge_slashes 只管斜杠,管不了 ...——那两个是RFC 3986要求的动作,不归这个开关管。

真正变化的是别的东西

关掉之后唯一实质性的变化,是 $uri 里保留了原始的多余斜杠。这件事的后果,nginx文档自己讲了:

Note that compression is essential for the correct matching of prefix string and regular expression locations. Without it, the "//scripts/one.php" request would not match location /scripts/ { ... } and might be processed as a static file.

翻译成人话:你所有按前缀写的location都会被绕过。那些挂在 location /admin/ 上的访问控制、挂在 location /api/ 上的限流、挂在某个前缀上的缓存策略,请求方只要多打一道斜杠就全部躲开了,而文件本身还是能取到。

文档随后给了一句相当罕见的措辞:

However, for security considerations, it is better to avoid turning the compression off.

官方文档很少直接说“你最好别这么干”。这条值得当成硬规则记下来:merge_slashes off 不是在收紧URL,它在放松你的location匹配。唯一的合理用例文档也写了——路径里嵌了base64,而base64的字母表里恰好有斜杠。

顺手说一句,“改一个开关,结果和直觉相反”这种事在nginx里不算稀奇。Nginx反向代理实战指南proxy_pass 末尾那道斜杠加不加,能把整个后端路径改掉,也是同一类。

现网105个站,一个页面平均有几个能返回200的地址?

实验台说明了机制,但机制不等于现实。真实站点前面挂着CDN、边缘规则、框架路由,最终行为得实测。

怎么测的

先抓181个域名的首页,从HTML里挑出一个同域的深层链接——要求至少两段路径、不带查询串、不是静态资源,这样才有路径可以变形。能拿到深层地址的有112个站,其中原样请求就能返回200的105个,这105个是分母。

然后对每个深层地址做八种变形,加原样一共九次请求,总计1008次。判据卡得比较严,一个变体要算数,必须同时满足三条:

  • 返回 200
  • 没有被重定向走(最终地址还是它自己,被301送回规范地址的不算);
  • 响应体的 SHA-1与原样请求完全一致(字节级相同,不是“看起来差不多”)。

第三条最关键。很多站会对变体返回一个“长得像但不是同一个”的页面,那种情况另有说法,不该混进来。

结果

额外写法数站数占比
0种(只有规范地址能进)4038.1%
1种1514.3%
2种2321.9%
3种1312.4%
4种21.9%
5种65.7%
6种65.7%

平均每站额外1.66种写法,也就是一个页面平均带着2.66个字节级完全相同的200地址。61.9% 的站至少多出一个。

按变体类型拆开看,哪种写法最容易蒙混过关:

变体原地200且字节相同占105站
尾斜杠切换(加上或去掉末尾的 /)6057.1%
加一个无意义查询串4845.7%
开头双斜杠 //a/b2321.9%
点段 /a/./b1716.2%
路径内双斜杠 /a//b1211.4%
父段 /a/zz/../b1211.4%
路径全大写21.9%
追加一段不存在的路径00.0%

尾斜杠是头号来源,一多半的站两种写法都收。这个数字后面还会再出现一次——它在另一个维度上的杀伤力比在这里大得多。

最松的和最严的

额外写法数拉满(6种)的六个站:shopify.comstripe.comklaviyo.combacklinko.comnodejs.orgiana.org

这份名单挺有意思。前三个是SaaS大厂,第四个是SEO圈子里人人都读过的博客,最后一个是管着全球IP地址和端口号分配的机构。连IANA都没把自己的URL收干净,这事说明它确实不是“谁不专业”的问题,而是默认行为就这样。

另一头,零额外写法的站包括 walmart.comtarget.comnike.comhubspot.comsalesforce.comsquare.comwix.com。大型电商和企业级SaaS明显更紧——它们的URL通常由一层专门的边缘路由统一发牌,不是让web服务器直接对着文件系统开门。

按Server头分组也能看出这个规律,只是样本量小,只能当参考。测量来自单一出口,跨境访问这批站点,个别站的边缘节点行为可能和当地不同,绝对值别当成普适结论——这一点在限速规则拒掉的第4个请求是robots.txt,全站SEO抓取会停12小时那次两地对照里吃过教训,同一批站从不同网络位置测,状态码有四分之一对不上。

canonical到底收不收得住这些变体?

数完了地址,下一个问题自然是:这些多出来的地址,有没有人管?

标准答案是canonical。我把它当成一个可证伪的假设来测:对每一个“原地200且字节相同”的变体,把它页面上的canonical取出来,解析成绝对地址,看它指回规范页,还是跟着变体一起变了。

结果和我下场之前的预期完全相反:

项目数量占比
原样页面带canonical的站87 / 10582.9%
变体上canonical指回规范页(收敛成功)130100.0%
变体上canonical跟着变(收敛失败)00.0%
变体页干脆没有canonical00.0%

130次判决,一次都没掉链子。我原本准备写的是“canonical在现网基本是坏的”,实测把这个预设按在地上摩擦了——凡是有canonical的站,它在这些路径变体上都老老实实指回了同一个规范地址。

为什么它这么稳

回头看第一节那张表就明白了:因为canonical通常是模板写死的绝对地址,或者由框架按路由名生成,而不是拿“当前请求的URL”拼出来的。路径变体在到达应用之前就已经被归一化成同一个 $uri,应用根本不知道自己是被 //a/b 还是 /a/./b 请求的,自然也就写不出跟着变的canonical。

这是个挺漂亮的意外收获:把请求写法抹平的那套归一化机制,同时也顺手保证了canonical不会被写歪。同一件事在这里既是病因又是解药。

那还有什么可担心的

两件事。

一是那17.1%。105个站里有18个原样页面就没有canonical。它们的路径变体没有任何收敛机制,全靠搜索引擎自己去判重。谷歌确实会自己选一个规范地址,那套决策逻辑在 Google选择Canonical URL的9大决策逻辑与排查实操指南里拆过,但“它会自己选”和“它会选你想要的那个”是两码事。

二是收敛成功不等于零成本。canonical是在页面被抓取之后才起作用的——爬虫得先把这个变体地址请求一遍、下载完整个页面、解析出canonical,才知道“哦这个不算数”。省下的是索引里的位置,花掉的是抓取。这笔账怎么算,做SEO算抓取预算,发现服务器把没改过的页面对爬虫重发了几百遍那篇讲得比较透。

所以canonical该有还得有,它是安全网;但它不是免死金牌,真要省抓取,还得靠301把变体挡在下载之前。

哪些写法是你自己配出来的?

前面数的都是“出厂默认送你的”。还有一类地址,是配置里那些看着人畜无害的行明确造出来的。这类通常比默认的严重得多,因为它们不受“路径必须真实存在”这个约束。

那条大小写不敏感的location

把某个路径写成不区分大小写,是个流传很广的写法,通常长这样:

location ~* ^/foo/bar {
    try_files /foo/bar.html =404;
}

看起来只是“让 /foo/bar/Foo/Bar 都能访问”。实验台上打一遍:

请求状态码$uri
/foo/bar200/foo/bar.html
/FOO/BAR200/foo/bar.html
/Foo/Bar200/foo/bar.html
/fOo/bAr200/foo/bar.html
/foo/barXXXX200/foo/bar.html

前四行是预期内的:六个字母,每个都能大小写互换,2的6次方等于64种写法全部返回200

最后一行才是真问题。~* ^/foo/bar 只锚定了开头,没锚定结尾,所以任何以它开头的路径都命中——/foo/barXXXX/foo/barbarbar/foo/bar-anything-you-want,全部200,全部返回同一份内容。

64种大小写组合乘以无限种后缀,这个页面的地址数量正式变成了无穷大。而这一切的代价,是配置里少写了一个 $

兜底路由

另一个常见来源是 try_files 的最后一档。第三个server用的是这样一条:

location / { try_files $uri $uri/ /app.html; }

意思是“文件找不到就交给应用处理”,几乎所有前后端分离的站、所有单页应用、大部分CMS都是这个结构。实测:

请求状态码返回字节
/this-page-does-not-exist200115
/a/b/c/d/e/f/g/h200115
/docs/guide/anything200115
/docs/guide/anything/deeper/still200115

任意深度、任意内容的路径,全部200,全部同一份字节。这条配置本身就是一台无限URL生成器,前提是有人给它喂地址——而“有人给它喂地址”这件事,恰恰是下一篇要讲的。

好消息是现网这么干的不多。我在105个站上做了同样的探测(在真实路径后面追加一段随机的不存在路径),只有12个站还返回200(11.4%),其中字节级完全相同的只有4个(3.8%):walmart.comengadget.comphp.netmariadb.com。剩下8个返回的是内容不同的页面,多半是自定义的错误页或者软404。

剩下91个站规规矩矩返回404。这说明大部分框架的兜底路由后面还接了一层“这个路由到底存不存在”的判断,没有直接把200发出去。HTTP状态码怎么影响SEO?301、302、404和410该怎么选里讲过为什么404在这个位置是对的答案。

还有一个挡住了

顺手测了一条路径遍历:/wp-admin/../../etc/passwd。nginx返回400,连 $uri 都没有输出。

这说明归一化不是无脑做的:.. 只要试图跳出根目录,请求就被直接拒了。停留在根目录之内的冗余段照单全收,跨出边界的立刻叫停。这个分界线画得很清楚,也解释了为什么 /foo/xx/../bar.html 能进而它不能。

该怎么把一个页面的地址收敛回一个?

前面数出来的东西,落到手上就是一张决策表。按“能不能挡在下载之前”排序,越靠前收益越大。

写法现网命中率建议动作说明
尾斜杠两种都收57.1%301到一种收益最大的一条,且下一篇会说明它的另一重代价
任意查询串都收45.7%canonical兜底不能一刀切301,正常参数要留
路径大小写原地20013.3%301到小写只有这13.3% 需要做,其余本来就404
多余斜杠 / 点段11%-22%canonical足够默认配置下拿不到原始写法,301做不了
location ~* 前缀正则配置决定$ 锚定结尾不加等于开放无限后缀
兜底路由吃掉一切3.8%路由不存在就返回404在应用层判断,不在nginx层

能直接抄的收敛配置

尾斜杠和大小写这两条是301能解决的,写法不复杂:

# 统一去掉末尾斜杠(目录除外),二选一,别两条都写
location ~ ^(.+)/$ {
    return 301 $1;
}

# 路径含大写字母就跳到小写版本
# 需要 nginx 带 perl 模块或在应用层做,纯 nginx 可用 map 列举常见入口
map $request_uri $has_upper {
    "~[A-Z]"  1;
    default   0;
}

大小写归一化在纯nginx里比较别扭,因为它没有内置的小写函数。实际项目里更常见的做法是在应用层或者CDN边缘规则里做。.htaccess重定向生成器实测:导入一份旧配置会丢掉多少规则里对照过Apache侧的等价写法。

两条铁律:

  • 只留一条规范形态,然后所有变体单跳到它。最忌讳的是A跳B、B又跳C,重定向链既费抓取又容易跳出环。
  • 301上线前先确认站内链接已经用的是规范形态,否则每一次内部跳转都在给自己制造跳转。

三条命令自查

不用写脚本,手动敲三次就能知道自己站在哪一档:

# 1. 尾斜杠:两个都 200 就是有问题
curl -s -o /dev/null -w '%{http_code}\n' https://你的域名/某个页面
curl -s -o /dev/null -w '%{http_code}\n' https://你的域名/某个页面/

# 2. 大小写:200 就要治,404 或 301 都合格
curl -s -o /dev/null -w '%{http_code}\n' https://你的域名/某个页面 | tr 'a-z' 'A-Z'

# 3. 兜底路由:必须 404
curl -s -o /dev/null -w '%{http_code}\n' https://你的域名/某个页面/随便一段不存在的

注意第一条和第三条要带 -o /dev/null 之外再看一眼字节数,因为“返回200”和“返回同一份200”是两件事。要严格一点就把 %{size_download} 也打出来,两次一样才算命中。

什么时候不值得治

说句实在话:如果你的canonical是模板写死的绝对地址,而且站内链接全部用规范形态,那多余斜杠和点段这两类基本可以不管。

理由是它们需要有人主动把那种畸形地址写出来才会被抓到。真实世界里,这种地址主要来自三个源头:手写外链时打错、某些聚合工具拼接出错、以及爬虫自己在解析相对链接时算出来的。前两个是小概率,第三个才是大头——而那正好是这个话题的另外一半,下一篇会拿真浏览器和两套URL标准对着测一遍。

保哥的经验是:先把尾斜杠和大小写这两条收干净,再确认每个页面都有canonical,剩下的按投入产出排到后面去。URL治理最容易犯的错是花三天写一套完美的重定向规则,去解决一个一年被抓两次的地址;而同一时间里,那个真正在漏抓取的分面参数还在那儿转。

常见问题解答

同一个页面有多个能返回200的地址,会被谷歌当成重复内容惩罚吗?

不会有惩罚这回事。谷歌的处理方式是从这批地址里挑一个当规范地址,其余的合并过去。真正的代价在抓取——每一个变体地址都要被完整下载一遍才能被判定为重复,这些请求全部计入你的抓取预算。所以问题不是“会不会被罚”,是“值不值得让爬虫为同一份内容下载好几遍”。

我加了canonical,是不是就不用管URL变体了?

基本可以,但不是全部。实测130次判决里canonical全部正确指回了规范地址,它比很多人以为的可靠。剩下的两个缺口是:一,你得先确认这个页面真的有canonical,现网有17.1% 的页面没有;二,canonical只能在页面被下载并解析之后生效,省的是索引不是抓取。命中率最高的尾斜杠变体值得用301挡在下载之前。

路径里的大小写到底要不要统一成小写?

看你的服务器怎么回答大写地址。如果返回404(现网63.8% 是这样),说明大小写本来就是敏感的,不存在两个地址同时可访问的问题,不用做。如果原地返回200(13.3%),那必须做,因为谷歌明确把 /APPLE/apple 当两个各有内容的地址。测一条curl就能分辨自己在哪一档。

把merge_slashes关掉能不能挡住多余斜杠的地址?

不能,而且会帮倒忙。实测关掉之后 //foo/bar.html 照样返回200,因为最终去磁盘取文件的是操作系统,Linux本来就把连续斜杠当一个。唯一的实质变化是 $uri 保留了多余斜杠,导致所有按前缀写的location被绕过——访问控制、限流、缓存策略全部失效。nginx官方文档直接建议出于安全考虑不要关它。

为什么我在nginx里写规则去跳转带双斜杠的地址,规则从来不生效?

因为归一化跑在你的配置之前。nginx在做location匹配之前就已经完成了百分号解码、点段解析和斜杠压缩,等你的rewrite或if拿到路径时,双斜杠已经不存在了。要拿原始写法只能用 $request_uri 这个变量,它保留了客户端发过来的原样请求行。

兜底路由(try_files最后指向应用)是不是必须改掉?

不用改nginx那一层,前后端分离和单页应用都得这么配。要改的是应用层:路由匹配不上时返回404,而不是把首页或者某个默认页当200发出去。现网105个站里只有3.8% 在这一点上失守,说明主流框架默认是对的,属于要自查但通常没问题的一项。

这些多出来的地址会不会稀释页面权重?

不会按“稀释”那个模型来。只要它们被合并到同一个规范地址,外链信号也会跟着合并过去。真正会损失的是两种情况:一是页面没有canonical且谷歌选错了规范地址,二是你的站内链接本身就在用不同写法互相指,导致内部权重流向分散在几个地址上。第二种比第一种常见得多,也更容易自查。

权威参考资料

分享到
标签
版权声明

本文标题:《同一个页面11种URL写法都返回200,做SEO查重复内容时一条都不会报》

本文链接:https://zhangwenbao.com/url-path-normalization-case-slash-duplicate-seo.html

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

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