站点地图里抽了585条网址,48条是坏的,而生成它的程序一次都没报错
本文目录
- 为什么偏偏挑站点地图来查?
- 为什么用Magento来做样本?
- 样本是怎么凑出来的
- 怎么确认这个接口的回答本身可信?
- 抽查585条之后,真实数字是多少?
- 为什么错误全压在两个站上,而不是均匀分布?
- 接口说页面正常,服务器为什么回404?
- 三个组件读同一张表,所以它们一致地错
- 为什么32条跳转里路由接口只看见了5条?
- 店铺切换制造的302占了大头
- 商品下架跳去分类页
- 名册本身就只认识三分之二
- 剩下的在应用够不着的层
- 清单自己跟自己打架的两种方式
- 一份文件里混着两种网址写法
- 提交的网址和页面自称的规范网址对不上
- 第一版我测出来是18.7%,错在哪里?
- 那份自己进不去的站点地图
- 自己怎么查一遍?
- 第一步 确认清单本身拿得到
- 第二步 看清单的整体形态
- 第三步 抽样逐条请求
- 第四步 看分布再决定怎么修
- 第五步 顺手对一下canonical
- 这套判断还能搬到哪里?
- 常见问题解答
- 站点地图里放了跳转网址,到底会不会被惩罚?
- 站点地图重新生成一遍能不能解决这些问题?
- 为什么不能直接用平台后台的网址重写列表来核对?
- 站点地图里出现内部裸路由是怎么回事?
- 抽样多少条才算够?
- 尾斜杠不统一真的要紧吗?
- 怎么判断一个自己没权限的接口值不值得信?
- 非Magento的站点怎么做同样的检查?
- 权威参考资料
摘要:拿13个真实Magento站的站点地图,随机抽585条网址逐条请求,48条不是200——15条404、1条500、32条在跳转。真正值得看的不是这个8.2%,是它的分布:5个站一条不错,2个站错到37.8%。这说明站点地图坏掉的方式不是慢慢腐烂,而是某一类配置从头就没对过。更麻烦的是,这批站自己的路由接口对其中9条404网址依然回答“认识,正常,不用跳转”——因为接口、站点地图生成器、后台列表读的是同一张表,它们只会一致地错。
做技术SEO的人有个共同的盲区:站点地图是唯一一份由我们主动递到搜索引擎手上的文件,却也是全站最少被人打开看过的文件。robots.txt只有几十行,谁都会读一遍;结构化数据有测试工具,改完就去跑;页面标题描述天天在报表里晃。只有站点地图,生成完就躺在那儿,几千上万行,除非Search Console报红,否则没人会一条条点开。
这次想验证的事很朴素:这份没人看的清单,到底有多少条是坏的。为了不靠感觉说话,得找一批真实站点,还得有办法在不登录后台的前提下问出“这个网址在系统眼里是什么”。Magento恰好两样都能满足——它的商户遍布欧美中小电商,而且它带一个免登录就能查的路由接口。
为什么偏偏挑站点地图来查?
先说清楚这份文件的分量。很多人把它当成“告诉搜索引擎我有哪些页面”的通讯录,交上去就完事。实际上它承担的角色比通讯录重得多。
Google在规范网址的选择逻辑里写得很直白:There are a handful of factors that play a role in canonicalization: whether the page is served over HTTP or HTTPS, redirects, presence of the URL in a sitemap, and rel="canonical" link annotations.
翻成人话就是:网址是否出现在站点地图里,本身就是判断哪一版才是正版的因素之一。
这句话把站点地图的性质变了。它不是通讯录,是投票。你往里面放一条网址,等于替它投了一票“这一版是正版”。那么放进去一条已经301到别处的旧网址,就不只是浪费一次抓取,而是在给一个已经退位的地址拉票,跟页面自己发出的301信号互相打架。
另一句更关键的话在构建站点地图的文档里:Use fully-qualified, absolute URLs in your sitemaps. Google will attempt to crawl your URLs exactly as listed.——照单抓,一字不改。你写www版它就抓www版,你多写一个斜杠它就带着那个斜杠去敲门。没有任何一环会替你把网址“理解”成你本来想写的样子。
所以这份文件的容错率其实是零。它既不能写错,也不能过期,而它偏偏是由程序批量生成、按cron定时刷新、从来没人肉眼校对的那一份。这三个条件凑在一起,就是典型的出事地形。
为什么用Magento来做样本?
选Magento不是因为它特别差,恰恰相反——它是少数几个把“这个网址是什么”做成公开接口的电商平台。Adobe的GraphQL文档里有个 route 查询,官方定义是returns the canonical URL for a specified product, category, or CMS page,给它一个路径,它回答三件事:这是商品、分类还是内容页;要不要跳转;规范地址是哪一条。
这个接口不需要令牌,一个GET请求就能问。对做审计的人来说,它相当于把平台内部那本路由名册摊开给你看。有了它,就能做一件平时做不到的事:把“系统认为这个网址是什么”和“服务器实际返回什么”并排放在一起比。
更早以前给一家做工业展示器材的B2B出海客户排查过类似问题,当时只能靠爬全站再和站点地图做差集,跑一夜才出结果。有了这种平台自带的只读接口,同样的活儿变成十几分钟,这也是这两年做技术审计最省力的一条路子:动手爬之前,先花十分钟找一找目标平台有没有公开的只读接口,多数时候有,而且比爬HTML干净十倍。
样本是怎么凑出来的
过程没什么玄机,就是笨办法。先攒了约285个欧美中小电商域名,逐个请求 /graphql?query={storeConfig{store_code}}。不是Magento的返回404或者一段HTML,是Magento的会规规矩矩吐一段JSON。这一步筛出16个确认在跑Magento 2且接口可查的站。
其中3个站的站点地图后来取不到(有一个还挺讽刺,下文会讲),最终进入统计的是13个站。每站从它自己robots.txt声明的站点地图里等距抽45条,合计585条。全部只发只读请求,不登录、不下单、不碰任何写接口。
顺带说一句,这16个站的网址后缀配置就已经三种形态并存了:有的是 .html,有的是斜杠,有的干脆是空。同一个平台,同一个配置项,三种答案。这个细节后面会回来找我们。
怎么确认这个接口的回答本身可信?
正式取数之前还有一道绕不开的坎。既然打算拿这个接口当尺子去量别人,那得先证明这把尺子是准的。要是它本身就在胡说,后面所有结论都白搭。
问题是,怎么验证一个你没有内部权限的黑盒接口?总不能打电话去问人家数据库里存了什么。
办法是找一批你事先就知道正确答案的问题去问它。这类样本构造起来并不难:把一条真实网址的路径全部改成大写。按Magento的机制,大写路径不会有独立的重写记录,正确答案必然是这不是规范地址、应该301回小写那一版。答案是已知的,接口答得对不对一眼就能看出来。
于是从11个站的站点地图里各取12条真实网址,转成全大写,同时问路由接口和实际发一次HTTP请求。结果相当漂亮:
| 大写变体的表现 | 条数 |
|---|---|
| 路由接口回答需要301、且规范地址是小写那条 | 90 |
| 其中服务器实际也返回3xx、且跳转目标一致 | 90 |
| 两层答案对不上的 | 0 |
90条全中,零分歧。接口不但正确判断出这是个非规范形态,还准确给出了应该跳去哪里,和服务器实际做的事一模一样。尺子是准的。
顺带还澄清了一个原本的误判。下场之前保哥的假设是:路由接口对大小写多半不敏感(数据库排序规则通常如此),所以它大概会把大写网址当成正版直接放行,而服务器会老老实实301——两层就此分叉,正好构成一个漂亮的坑。实测把这个假设干净利落地否掉了,两层完全一致,根本没有坑。那半天的准备算是白做,但换来一把验过的尺子,不亏。
这件事值得单独记一笔:要用一个新工具去测量未知,先拿它测一批你已经知道答案的东西。校准这一步的性价比高得离谱——它既可能让你对后续所有数据都放心,也可能当场救你一命,免得拿一把歪尺子量出一堆看着很有道理的结论。而它的成本,通常就是构造几十条已知答案的样本而已。
抽查585条之后,真实数字是多少?
逐条请求的结果如下。这里的每一条都是直接拿站点地图里 <loc> 标签的原文去请求的,没有做任何改写——这一点很重要,后面有一整节专门讲我在这上面栽的跟头。
| 返回状态 | 条数 | 占比 |
|---|---|---|
| 200正常 | 537 | 91.8% |
| 302临时跳转 | 17 | 2.9% |
| 301永久跳转 | 15 | 2.6% |
| 404页面不存在 | 15 | 2.6% |
| 500服务器错误 | 1 | 0.2% |
合计48条不是200,占8.2%。其中16条是彻底的死链(404加500),32条在跳转。
8.2% 这个数字,说实话比我下场之前预估的要低。做这行久了容易有种悲观直觉,觉得没人管的文件一定烂得不成样子。事实是大部分站的站点地图相当干净。但把这48条摊到13个站上,画面立刻就变了。
为什么错误全压在两个站上,而不是均匀分布?
如果站点地图真的是“随时间慢慢腐烂”,那8.2% 应该大致均匀地散在各站,每个站坏个三五条。实际分布是这样:
| 错误率 | 站点数 | 具体 |
|---|---|---|
| 0.0% | 5 | anglingdirect、bx.eu、case24、heals、luxuryflooring |
| 2.2%(45条错1条) | 4 | barrdisplay、bestonlinecabinets、bulk、wmf |
| 11.1%(错5条) | 2 | coxandcox、thespacecollective |
| 37.8%(错17条) | 2 | dunkin、sousvidetools |
5个站一条不错,4个站各错1条(那1条基本都是真的下架商品,属于正常损耗),然后断崖式地跳到2个站错掉三分之一以上。这不是腐烂曲线,这是两种完全不同的病凑在了一张表上。
把这两个高错误率的站拆开看,病因也确实不是“某几条过期”。dunkin那17条里,11条404、6条跳转,坏的方式高度雷同;sousvidetools那17条清一色是302,而且跳转目标带着同一个查询参数。它们不是坏了17条网址,是有一整类东西从一开始就没配对。
这里能提炼出一条挺通用的判断:配置类的错误是全有全无的,数据类的错误才是渐变的。看到一个指标8.2%,第一反应通常是“去把那8% 修掉”,但先得看这8% 是摊平的还是堆在几个对象上。摊平说明是数据在自然衰减,该建的是定期清理机制;堆在少数几个上,说明是某个开关拨错了,修一次就归零,建再多机制也没用。
这个判断保哥后来在别的场合验证过好几次,包括排查WooCommerce归档页的索引膨胀时也是同一套逻辑:先看分布形状,再决定要不要动手修单条。
接口说页面正常,服务器为什么回404?
这是整轮测试里最让人后背发凉的一组数据。
dunkin的站点地图里那11条404,我顺手也问了 route 接口。按常理,页面都404了,平台的路由接口总该说“查无此项”吧。结果是:11条里有9条,接口回答的是“认识,类型正常,redirect_code为0,不需要跳转”。
| 网址 | 路由接口的回答 | 服务器实际返回 |
|---|---|---|
| /glazed-munchkinsr | 认识,无需跳转 | 404 |
| /chai-latte | 认识,无需跳转 | 404 |
| /easter-nest | 认识,无需跳转 | 404 |
| /salted-caramel-muffin | 认识,无需跳转 | 404 |
| /thermalmug | 认识,无需跳转 | 404 |
把范围放大到全部585条,这类“接口说好好的、实际不是200”的一共29条:19条实际在跳转、9条404、1条500。
三个组件读同一张表,所以它们一致地错
原因不难推。Adobe的文档里写着 When you create a URL rewrite, Commerce automatically creates a permanent redirect (301) so that any links pointing to the old URL are redirected to the new address.——网址重写记录是系统自动维护的。问题在于,商品被停用或删除之后,那条重写记录并不会跟着消失,官方文档对这种情况也没有交代。
于是就出现了这样一条链子:重写表里还留着记录 → 站点地图生成器从这张表取数,把它写进清单 → 路由接口也查这张表,于是回答“认识” → 后台的网址重写列表同样从这张表读,看上去一切正常 → 只有真正去请求那个地址的人,才会撞上404。
四个地方交叉印证,三个说没问题,唯一说有问题的是唯一没读那张表的那一个。
这就引出一条我觉得比这次所有数据都值钱的经验:多个组件给出一致的答案,不代表答案是对的。如果它们读的是同一个数据源,一致性只证明它们之间没打架,不证明数据是真的。真正的交叉验证必须跨数据源,同源的相互印证只是自我安慰——而且是最危险的那种自我安慰,因为它看起来特别像验证过了。
放到日常工作里,这条能直接翻译成一个动作:凡是要确认“某个页面在不在、是什么状态”,最终那一次核对必须是从站外发起的一次真实HTTP请求。后台列表、平台接口、导出的报表,全都不算数,因为它们和你要验证的对象共用同一个真相。
为什么32条跳转里路由接口只看见了5条?
既然接口能查规范地址,那用它来体检站点地图应该很顺手才对——这正是我下场时的原始设想。结果这个设想被自己的数据推翻了。
32条实际在跳转的网址里,路由接口提前预警的只有5条,剩下27条它完全没看见。命中率15.6%,作为体检工具基本等于失效。
把这27条拆开,原因一目了然:
店铺切换制造的302占了大头
sousvidetools那17条全是这个模式。请求 /charcoal-bbqs,服务器302到 /charcoal-bbqs?___store=us&___from_store=en。这是Magento按访客地区自动切换店铺视图的机制,跳转发生在应用入口之前,跟具体哪个网址没关系。
这件事对搜索的杀伤力被严重低估了。Googlebot主要从美国IP抓取,也就是说它每次去敲站点地图里那个干净网址,拿到的都是一个302,从来没到达过清单上声明的那一版。
而Google对临时跳转的处理是 Temporary redirects: Show the source page in search results——搜索结果里显示的仍是源地址,用户点进去再被甩到带参数的版本。站点地图交的是A,能被访问到的是B,搜索结果展示的还是A,三方错位,而且没有任何一方会报错。
这个站还顺手贡献了另一个发现。它的清单里躺着6条形如 /catalog/product/view/id/1026 的网址——这是Magento在找不到重写记录时的内部裸路由,本来只该在系统内部周转,正常情况下用户永远看不到它。它们出现在提交给搜索引擎的清单里,说明生成器在取数时对某些商品没拿到漂亮网址,于是退而求其次写了裸路由,而这一步降级同样是静悄悄发生的。
商品下架跳去分类页
coxandcox的5条属于这一类,全是商品页301到它所属的分类页。比如一条羊毛地毯的商品页跳到了地毯分类,一个铸铁球形把手跳到了五金配件分类。
这个做法本身在圈里有争议,倒不算错得离谱,但把旧商品网址继续留在站点地图里就说不过去了——你一边告诉搜索引擎“这条是正版”,一边又用301说“它搬到分类页去了”。这两件事同时做,等于自己跟自己抬杠。真要处理下架商品,301、410和软404之间怎么选是另一个话题,但无论选哪条路,站点地图里都该把它删掉。
名册本身就只认识三分之二
还有一组数字能说明路由接口的覆盖边界。585条网址里,接口能认出来的只有372条,占63.6%。剩下三分之一它回答的是查无此项或者干脆返回空。
这些认不出来的网址绝大多数活得好好的——193条实际返回200,页面完整、内容正常。它们只是不在平台的路由名册上:博客模块自己接管的路径、第三方插件注册的路由、多店视图带前缀的地址、还有前端框架自己处理的那部分。
换句话说,这本名册记录的从来就不是“这个站有哪些页面”,而是“哪些页面归我管”。拿它当全站清单用,一开始方向就偏了。这跟排查内容管理系统换成前后端分离之后那套基建时遇到的问题同源:前端换了人做,后端那本册子还停在原地,而没人觉得对齐这件事该由自己负责。
剩下的在应用够不着的层
还有几条落在主机名和斜杠这些地方。route 接口只接收一个路径,它压根不知道请求是走www还是不走www,也管不着Web服务器的重写规则会不会给你补一个尾斜杠。
把这三类摆在一起,结论就清楚了:站点地图里的网址,最终形态是由DNS、CDN、Web服务器重写规则、应用路由这四五层共同决定的,而生成这份清单的程序只住在最里面那一层。它没有能力校验自己的输出,不是因为写得不好,是因为它站的位置就看不见外面。
这也解释了为什么“把站点地图重新生成一遍”对这类问题完全无效。Adobe的文档还特意叮嘱 Your site map should be updated as frequently as the content on your site changes,生成得再勤快也没用——它每次都会一模一样地把同一个错误再写一遍,因为错误不在它的视野里。这个道理和后端与SEO协作时那几个交接点要处理的是同一件事:责任分层之后,中间那道缝没人认领。
清单自己跟自己打架的两种方式
前面查的都是“这条网址能不能打开”。还有一类问题跟状态码无关——网址好好的,200返回,内容也正常,但清单自己在跟自己较劲。这类问题不会在任何工具里飘红,只能靠手动对一遍。
一份文件里混着两种网址写法
这个细节是顺手统计出来的,本来没打算写,看到数字之后觉得必须留下来。把每个站站点地图里全部网址的尾斜杠情况数了一遍:
| 站点 | 网址总数 | 以斜杠结尾 | 占比 |
|---|---|---|---|
| sousvidetools | 2154 | 1267 | 58.8% |
| luxuryflooring | 1033 | 127 | 12.3% |
| coxandcox | 6863 | 6764 | 98.6% |
| anglingdirect | 25639 | 67 | 0.3% |
| heals | 1198 | 0 | 0.0% |
heals是0%,干净利落,全站一种写法。anglingdirect两万多条里只有67条异类,也还说得过去。但sousvidetools是58.8%——同一份文件里,一半多带斜杠,剩下一半不带。
一份由程序统一生成的文件不会长成这样。程序只会有一种写法。混着两种,只能说明这份清单是拼起来的:一部分来自平台原生的生成器,另一部分来自某个插件、某次导入,或者某段没人记得是谁写的脚本。它们对网址后缀的处理规则不一样,谁都没错,凑在一起就错了。
形态不一致等于有多个作者。这个判据用起来非常快,而且不限于站点地图——日志格式、导出的报表、配置文件,凡是本该由单一程序产出的东西,一旦内部形态出现两套,第一反应就该是去找那个你不知道存在的第二作者,而不是去修那些不一致的条目。
提交的网址和页面自称的规范网址对不上
537条正常返回200的网址里,顺手对了一下页面上的canonical标签:488条自指(健康),8条指向别处,41条页面上压根没有canonical。
那8条里有7条来自同一个站,模式完全一样:站点地图提交的是 /au/cases/apple/iphone-xr/custom,而页面自己的canonical写的是 /cases/apple/iphone-xr/custom,少了澳洲站的路径前缀。
换成人话,这个站在同时说两句话:站点地图说“请收录澳洲版这一条”,页面自己说“别收我,正版是主站那一条”。这类多店视图下的信号打架,在Magento多店架构里格外容易出现,因为站点地图是按店铺分开生成的,而canonical的取值逻辑往往是全局的一套。
好在这类冲突的后果相对可控。Google自己说得很清楚:indicating a canonical preference is a hint, not a rule——你给的都只是提示,最终它会自己挑。真正的代价不是它挑错,而是你的信号自相矛盾之后就失去了发言权,接下来它选哪一版全看它高兴。至于它是按什么在选,规范网址的判定逻辑是另一个值得单独读一遍的话题。
另外那41条没有canonical的,倒不构成硬伤,只是白白少了一个本可以主动给出的信号。
第一版我测出来是18.7%,错在哪里?
这一节本来不在提纲里。写到这儿必须补上,因为它是这轮测试里我自己犯的、也最容易被别人重复犯的错。
第一版脚本是这么写的:从站点地图里解析出每条网址的路径部分,再拼到站点的主域名上去请求。看起来天经地义,也省得处理各种奇怪的绝对地址。跑出来的结果是18.7% 不正常,我当时还觉得这数字挺有冲击力。
后来核查具体案例时发现不对劲。bx.eu那个站,站点地图里850条网址的主机名全部是 bx.eu,而我脚本里存的站点入口是 www.bx.eu。于是我把路径拼到了带www的域名上,站点老老实实301到不带www的版本,我的脚本就忠实地记录下“24条在跳转”。
那24条跳转是我自己造出来的。改成直接请求 <loc> 标签里的原文之后,bx.eu变成45条全部200,一条不错。整体数字从18.7% 落到8.2%——我的测量方法凭空制造了10个百分点的假阳性。
教训写成一句话:要检查一份清单里的值对不对,就必须原样使用清单里的值。任何形式的重新拼装,都会把你自己的假设混进结论里,而且混得毫无痕迹。更阴险的是,这种错误只会让指标变难看,不会让它变好看,所以它永远伪装成“发现了问题”,你不会有任何动机去怀疑它。
顺带一提,luxuryflooring那个站还给我上了另一课:它的站点地图里1033条网址,主机名全部是 luxuryflooring.co.uk,而我进站的域名是 luxuryflooringandfurnishings.co.uk。这次它们都是200,没造成假阳性,但如果我还在用拼装法,这个站会被我判成1033条全坏。
那份自己进不去的站点地图
还有两个站值得单拎出来说,它们的问题连一条网址都轮不到。
douglas.bg的robots.txt里规规矩矩声明了三份站点地图,我去取,三份全部返回403。反复试了几次都一样,是WAF在拦。也就是说这个站在robots.txt里公开告诉搜索引擎“我的清单在这三个地址”,然后又派了个保安把去取清单的人挡在门外。搜索引擎当然也是被挡的那一个。
thespacecollective的情况轻一点:声明的站点地图地址本身返回301,跟随之后才是200。能取到,只是多绕一跳。同一个站的抽样里还有4条404,其中3条是 /blog/archive/2020-12 这样的月份归档页——归档页在博客模块里生成,在站点地图里被收录,然后在某次改版中被取消了,三方各走各的。
这两个站合起来说明一件事:体检站点地图的第一步不是抽查条目,是先确认这份文件本身能不能被拿到、拿到的是不是完整的那一份。保哥一开始就是直接跳进去抽条目的,差点漏掉这两个更严重的问题。
douglas.bg那种情况尤其值得警惕,因为它在自己人眼里是完全正常的。运营在浏览器里点开清单地址,带着完整的浏览器指纹和Cookie,防护系统客客气气放行,看到的是一份好端端的XML。只有不带这些特征的请求才会吃403,而搜索引擎的抓取工具恰恰就是不带这些特征的那一类。
这类问题在任何后台报表里都不会露头,也不会触发告警,因为从系统的角度看压根没出错——文件在,服务在,只是有一类访客进不来,而那类访客正好是你最想请进来的。验证一份对外文件能不能被拿到,必须用最朴素的那种请求去试,越像自己人的请求越测不出问题。
自己怎么查一遍?
整套流程不需要任何付费工具,一个能发HTTP请求的脚本就够。按下面的顺序做,先大后小,别一上来就抠细节。
第一步 确认清单本身拿得到
读自己的robots.txt,把里面声明的每一个站点地图地址挨个请求一次。要看的是不跟随跳转时的原始状态码:403说明被自己的防护挡了,301说明声明的地址不是最终地址,404说明声明的东西根本不存在。这一步花不了两分钟,却是漏得最多的一步。
第二步 看清单的整体形态
把所有 <loc> 解析出来,先不请求,只统计三件事:
- 主机名有几种。答案必须是1。出现两种就说明有配置或者有第二作者。
- 协议有几种。答案必须是1,而且是https。
- 尾斜杠的比例。要么接近0%,要么接近100%,落在中间就是拼接出来的。
这三个数字加起来不到十行代码,却能一眼看出这份清单是不是单一程序产出的。前面13个站里,光靠这一步就能把sousvidetools和luxuryflooring挑出来。
第三步 抽样逐条请求
等距抽样,别只取开头——站点地图通常按分类或时间排序,只看前50条会系统性地漏掉尾部那批老网址。抽50到100条足够看出分布。
请求时有三个硬要求:直接用 <loc> 原文,不要重拼;不跟随跳转,先看第一跳的状态码;从站外发起,不要用任何后台接口代替。这三条每一条都是我这次踩出来的。
第四步 看分布再决定怎么修
拿到错误清单后先别急着逐条改。把错误按三个维度各分一次组:按目录前缀分(是不是某个栏目整体出了问题)、按内容类型分(商品页、分类页还是内容页)、按状态码分(404和302通常是完全不同的两件事)。
只要有任何一个维度上出现明显的堆积,那就是配置问题,去找那个开关,改一次全部归零。三个维度都摊得很平,才轮到建定期清理的机制。这个顺序反过来做,代价是你会先花两周写一套清理脚本,然后发现改一个配置项就够了。
去年帮一家做工业配件的B2B出海客户过这套流程,第二步就卡住了:他们清单里的主机名有两种,一种带www一种不带。往回追,是营销团队为了投放单独加过一份手工维护的清单,两份文件各写各的,谁都不知道对方存在。这种问题逐条抽查一年也查不出来,统计一下主机名种类,十秒钟就露馅了。
第五步 顺手对一下canonical
对返回200的那批,取页面上的canonical和清单里的地址比一比。不一致的说明两个信号在打架,这类问题不会报错,也不会在任何工具里飘红,只能靠这样手动对一遍。
如果嫌手写脚本麻烦,站内那个可索引性一键体检工具能覆盖单页维度的检查,批量那部分还是得自己写几行。
这套判断还能搬到哪里?
这次的具体结论是Magento的,但真正能带走的三条跟平台没关系。
第一条,同源的交叉验证是自我安慰。后台说在、接口说在、报表说在,只要它们读的是同一张表,这三票加起来还是一票。想验证一件事是不是真的,验证路径必须和被验证的对象走不同的数据源。这条在排查收录数据到底该信谁的时候同样成立。
第二条,看到一个百分比,先看它的分布形状再看它的大小。8.2% 和8.2% 可以是完全不同的两件事:均匀摊开的是数据在衰减,堆在两个对象上的是配置拨错了。前者要建机制,后者改一次就归零。搞混了会在错误的方向上投入大量精力,而且因为指标确实在缓慢改善,你还会以为自己走对了路。
第三条,没有任何一层能校验自己的输出。站点地图由应用生成,但网址的最终形态是四五层共同决定的,应用只能看见自己那一层。这不是Magento的缺陷,任何分层系统都这样。推论是:凡是要验收一个跨层的产物,验收动作必须站在最外层做——对网页来说,最外层就是一次从公网发起的、不带任何特权的普通请求。
最后补一句边界。这次全部数据来自公开只读请求,样本13个站也不算大,8.2% 这个数字请当成一个量级参考,别拿去当行业基准。真正建议照搬的是那套检查顺序:先确认清单拿得到,再看清单的形态,最后才抽样查条目。顺序反了的话,你会花很多时间去修那些根本不是主要矛盾的条目——这正是我第一版干的事。
常见问题解答
站点地图里放了跳转网址,到底会不会被惩罚?
不会有惩罚这回事。代价是两个层面的:一是浪费抓取,搜索引擎每次按清单去敲门都吃一个跳转;二是信号矛盾,因为网址出现在站点地图里本身就是规范网址的判断因素之一,你等于在给一个已经退位的地址投票,跟页面发出的301互相抵消。少量存在无所谓,成规模就该清理了。
站点地图重新生成一遍能不能解决这些问题?
看问题出在哪一层。商品下架、网址改名这类应用内部的变化,重新生成确实能跟上。但主机名写错、尾斜杠不统一、店铺切换造成的跳转,重新生成一万次也是同一个结果——生成程序看不见应用之外的那几层,它只会一模一样地把同一个错误再写一遍。
为什么不能直接用平台后台的网址重写列表来核对?
因为后台列表和站点地图生成器读的是同一张表。这次13个站里就抓到29条网址,平台接口回答“认识、正常、不需要跳转”,实际请求却是404或者跳转。同源的相互印证只能证明它们没打架,证明不了数据是真的。最终核对必须是一次从站外发起的真实请求。
站点地图里出现内部裸路由是怎么回事?
指的是 /catalog/product/view/id/1026 这类地址。它是平台在拿不到漂亮网址时的兜底路由,正常只在系统内部周转。它出现在提交给搜索引擎的清单里,说明生成器取数时对某些商品没查到重写记录,于是静悄悄降级写了裸路由。这类地址通常没有独立的规范标记,还会和正式商品页构成重复内容。发现之后别只删清单里那几条,要回头查这批商品为什么没有生成重写记录。
抽样多少条才算够?
看目的。想估算整体错误率,等距抽50到100条通常就能看出量级和分布形状。想彻底清理,那就得全量跑,几万条网址用并发脚本也就是几十分钟的事。要注意的是必须等距抽样,因为站点地图一般按分类或时间排序,只取开头会系统性地漏掉最容易过期的那批老网址。
尾斜杠不统一真的要紧吗?
它本身危害有限,多数服务器会用301把两种形态规范到一种。真正的价值在于它是个信号——一份程序生成的文件内部形态不该有两套,出现两套说明这份清单是拼起来的,背后有一个你可能不知道的第二来源。顺着这个线索往下查,通常能找到更值得修的东西。
怎么判断一个自己没权限的接口值不值得信?
拿一批你已经知道正确答案的样本去问它。构造这种样本通常比想象中容易,比如把网址改成大写、故意拼错一个字符、请求一条肯定不存在的路径,这些情况的正确答案都是确定的。这次校准用了90条大写变体,接口全部答对,才敢用它去测未知的部分。跳过这一步直接开测,等于拿一把没校过的尺子量东西,量出来的数字越精确越危险。
非Magento的站点怎么做同样的检查?
第二步到第五步跟平台完全无关,任何站点都能照做,因为它们全部基于站外的HTTP请求。唯一有平台依赖的是拿路由接口做交叉参考那部分,而这次的数据恰好说明那部分的价值有限——32条跳转它只预警了5条。换句话说,平台无关的那几步才是主菜。
权威参考资料
- Google搜索中心 · 构建并提交站点地图——明确要求使用完整绝对网址,并说明会严格按照清单里写的样子去抓取。
- Google搜索中心 · 网址规范化机制——列出参与规范网址判定的几项因素,其中包含网址是否出现在站点地图中。
- Google搜索中心 · 重定向与搜索——说明永久跳转与临时跳转在搜索结果里分别显示哪一个地址。
- Adobe Commerce文档 · 站点地图配置——说明站点地图由cron定时重新生成,以及应随内容变化更新的频率建议。
- Adobe Commerce GraphQL · route查询——本文用来查询平台路由名册的接口,返回实体类型、跳转代码与规范地址。
- Adobe Commerce文档 · 网址重写——说明修改网址标识时系统会自动生成301重写记录的机制。
本文标题:《站点地图里抽了585条网址,48条是坏的,而生成它的程序一次都没报错》
本文链接:https://zhangwenbao.com/magento-sitemap-url-audit-dead-links-redirects.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0