
先说结论这个站的规范网址不带末尾斜杠但有三处输出各写各的。TranslatePress 把网址转换成另一种语言时统一补斜杠所以 hreflang 标签里的翻译版本带斜杠SEO Pack 往站点地图里补语言版本用的是同一个函数翻译版本的条目也带斜杠主题的链接函数和模板又让导航、页脚、按钮的网址带着斜杠。除站点根和语言根外带斜杠的网址都会被 301 到不带斜杠的版本站点自己发出的信号互相矛盾站内每个入口还要多跳一次。修法是先定一个唯一写法再让每个出口服从它主题里的链接函数改掉源头TranslatePress 的两个过滤器统一它的输出站点根和语言根/en/作为例外保留斜杠。需要说明的是“已发现 - 尚未编入索引”的原因很多斜杠不一定是主因它的好处是容易自查值得先排除。现象站点用 WordPress TranslatePress 商业版默认语言放在根路径其他语言用/en/这样的子目录。站点地图由 SEOPress 输出各语言版本的条目由 TranslatePress 的 SEO Pack 补进去。固定链接结构的末尾没有斜杠页面网址是/pricing这种形式。Search Console 的“页面索引编制”报告里有两个现象服务页、报价页这类核心页面长期停在“已发现 - 尚未编入索引”。另有一批网址被归入“网页会自动重定向”。基础项先排除页面返回 200没有noindexrobots.txt也没有拦截这几项在这个站上现在都是正常的。所以问题不太像“抓不到”更像是站点给出的网址信号不干净。怎么定位第一步看带斜杠的网址跳到哪里“网页会自动重定向”直接指向跳转先拿一个翻译页面验证curl-sIhttps://example.com/en/pricing/|grep-iE^(HTTP|location|x-redirect-by)输出HTTP/2 301 location: https://example.com/en/pricing x-redirect-by: WordPressx-redirect-by: WordPress说明这个 301 是 WordPress 自己发的固定链接结构末尾没有斜杠它把带斜杠的请求归一到不带斜杠的形式不带斜杠的网址直接返回 200。用-L配合%{num_redirects}可以数出一共跳了几次这个例子里是一次curl-sIL-o/dev/null-w%{num_redirects} %{url_effective}\nhttps://example.com/en/pricing/所以带斜杠的网址不是坏链接只是每次都多一跳。真正要查的是谁在对外发带斜杠的网址。第二步找出站内带斜杠的链接把首页 HTML 里指向本站、路径以斜杠结尾的链接列出来。语言根/en/本来就该带斜杠先排除掉curl-shttps://example.com/\|grep-oEhref(https://example\.com)?/[^#?]/\|grep-vEhref(https://example\.com)?/(en|zh)/\|sort|uniq-c|sort-rn修复前导航、页脚和各处按钮的链接都在这条命令的输出里。主题源码里也可以直接搜写死的写法grep-rnEhome_url\( */[^]/ *\)wp-content/themes/your-theme--include*.php每种语言的首页和一两个内页都要跑一遍。第三步并排对比 hreflang、站点地图和 canonical在同一个翻译页面上把三处写法放在一起看目标是它们指向同一个、而且直接返回 200 的网址curl-shttps://example.com/en/pricing|grep-oElink rel(canonical|alternate)[^]*curl-shttps://example.com/page-sitemap1.xml|grep-Eloc|hreflang|head-n8canonical 也要核对它应当指向直接返回 200 的那一种写法。修复前hreflang 备选和站点地图里翻译版本的网址末尾多了一个斜杠指向的是一个会跳转的网址。整理下来是这样位置由谁生成修复前修复后导航、页脚、按钮主题的链接函数和模板带斜杠不带hreflang 标签中的翻译版本TranslatePress带斜杠不带站点地图里翻译版本的loc和 hreflang 备选TranslatePress SEO Pack带斜杠不带原因四个来源叠在一起WordPress 的规范形式不带斜杠。固定链接结构末尾没有/所以带斜杠的请求会被 301 回不带斜杠的形式。TranslatePress 的get_url_for_language()把网址转换成另一种语言时路径末尾一律补斜杠。不加任何过滤器实测不带语言前缀的网址转成/en/版本输出是/en/xxx/从翻译页面转回默认语言输出也带斜杠输入本来就是目标语言的网址斜杠保持输入的样子。hreflang 标签、站点地图和语言切换都调用这个函数。SEO Pack 往 SEOPress 的站点地图里补语言版本时loc和 hreflang 备选走的也是这个函数所以同一个站点地图里默认语言的条目不带斜杠翻译版本的条目带斜杠。主题一侧有三处example_page_url()拼地址时在末尾硬加/example_option_url()把主题选项里填的路径原样接到域名后面而选项默认值本身就写着/tools/这样的路径导航模板里还有一排写死的home_url( /xxx/ )。这些重定向本身不算错误Google 会跟着跳过去。但站点把大量入口都指向一个会跳转的网址hreflang 又要求各语言版本互相指向、写法对得上信号不干净抓取也花在跳转上。我们的判断是这至少增加了不确定性它是不是“尚未编入索引”的主因在 Search Console 里没有办法直接证明。修复统一写法放在一个 mu-plugin 里换主题不会丢主题里的函数再回头调用它。下面的函数名都加了example_前缀逻辑和实际使用的一致去掉了业务相关的部分。一个归一化函数规则除站点根和语言根外路径不带末尾斜杠?和#之后的内容原样保留。语言根必须例外因为/en会被 301 回/en/去掉它反而制造新的跳转。语言根从 TranslatePress 设置的url-slugs里读不写死。functionexample_untrailing($url){if(!is_string($url)||$url){return$url;}// 站点根和语言根/en/ 之类保留斜杠static$rootsnull;if(null$roots){$settingsget_option(trp_settings);$slugsisset($settings[url-slugs])is_array($settings[url-slugs])?array_values($settings[url-slugs]):array();$rootsarray_merge(array(/),array_map(staticfn($slug)/.strtolower(trim((string)$slug,/))./,$slugs));}$cutstrcspn($url,?#);$basesubstr($url,0,$cut);$path(string)wp_parse_url($base,PHP_URL_PATH);if($path||/!substr($path,-1)||in_array(strtolower($path),$roots,true)){return$url;}returnrtrim($base,/).substr($url,$cut);}这个函数会在每个链接上被调用所以语言根用static只算一次。统一 TranslatePress 输出的翻译网址hreflang 标签、站点地图和语言切换都走get_url_for_language()它在返回前会触发trp_get_url_for_language过滤器挂一个回调就能统一。优先级设成 999同一个过滤器上SEO Pack 的 slug 管理器在优先级 10 处理翻译 slug放在 999 拿到的就是最终网址。add_filter(trp_get_url_for_language,staticfunction($new_url,$source_url,$language){returnexample_untrailing($new_url);},999,3);x-default这个站的 x-default 由主题在wp_head优先级 9 输出拼网址用的是主题自己的函数所以单独处理。mu-plugin 先于主题加载要等到after_setup_theme触发时主题的functions.php已经执行完才能把主题注册的输出摘掉。这里是摘掉再重新输出而不是在旁边再加一个避免同一页出现两个 x-default。add_action(after_setup_theme,staticfunction(){// 摘掉主题原来的输出换成归一化之后的版本remove_action(wp_head,example_output_x_default_hreflang,9);add_action(wp_head,staticfunction(){if(is_admin()||is_feed()||is_404()||is_search()){return;}if(!function_exists(example_get_trp_settings)||!function_exists(example_language_switcher_url)){return;}$settingsexample_get_trp_settings();$default_language!empty($settings[default-language])?(string)$settings[default-language]:en_US;$default_urlexample_untrailing((string)example_language_switcher_url($default_language));if(!$default_url){printf(link relalternate hreflangx-default href%s/.\n,esc_url($default_url));}},9);},100);example_get_trp_settings()和example_language_switcher_url()是主题里已有的两个函数前者读取 TranslatePress 的设置后者返回当前页面在指定语言下的网址。站点地图SEO Pack 在每个url条目生成完之后会触发trp_xml_sitemap_output_for_url过滤器把加好语言版本的这一条 XML 传进来。回调里用正则找出本站网址里以斜杠结尾、后面紧跟/loc或引号hreflang 的href属性的部分交给同一个归一化函数。add_filter(trp_xml_sitemap_output_for_url,staticfunction($new_output,$output,$settings,$alternate,$all_lang_urls,$url){if(!is_string($new_output)||$new_output){return$new_output;}$hostpreg_quote((string)wp_parse_url(home_url(/),PHP_URL_HOST),~);$pattern~https?://.$host./[^\s\]*/(?(?:/loc|[\]))~i;return(string)preg_replace_callback($pattern,staticfunction($match){returnexample_untrailing($match[0]);},$new_output);},999,6);主题里的链接函数源头也要改只靠过滤器兜底修不到主题里的硬编码链接。页面链接函数只改一行function example_page_url( $page_key ) { $page_key sanitize_key( $page_key ); $definition example_get_page_definition( $page_key ); if ( home $page_key ) { return home_url( / ); } if ( empty( $definition[slug] ) ) { return home_url( / ); } - return home_url( / . trim( $definition[slug], / ) . / ); return home_url( / . trim( $definition[slug], / ) ); }example_option_url()在取值、回退之后还有一步把旧的静态文件名映射成页面网址这一步调用的就是上面的example_page_url()不用单独改示例里省略。改动只在两处返回的地方if ( preg_match( #^(https?:)?//#i, $normalized ) || preg_match( #^(mailto|tel):#i, $normalized ) ) { // 本站的绝对网址也过一遍外部网址、mailto:、tel: 原样返回 if ( function_exists( example_untrailing ) wp_parse_url( $normalized, PHP_URL_HOST ) wp_parse_url( home_url(), PHP_URL_HOST ) ) { return example_untrailing( $normalized ); } return $normalized; } - return home_url( / substr( $normalized, 0, 1 ) ? $normalized : / . $normalized ); $url home_url( / substr( $normalized, 0, 1 ) ? $normalized : / . $normalized ); return function_exists( example_untrailing ) ? example_untrailing( $url ) : $url;导航模板里写死的home_url( /xxx/ )用第二步的 grep 找出来逐个去掉斜杠。语言切换函数的返回值也过一遍归一化if ( $target_url ) { return home_url( / ); } - return $target_url; return function_exists( example_untrailing ) ? example_untrailing( $target_url ) : $target_url;这几处都加了function_exists判断归一化函数在 mu-plugin 里万一 mu-plugin 被移走主题只是退回旧写法不会报致命错误。修完怎么验证把前面三步的命令原样再跑一遍预期是这样带斜杠的网址仍然 301 到不带斜杠的版本一跳。这条 301 要留着外部的旧链接还会用到。首页和/en/首页的 grep 输出为空。canonical、hreflang 和站点地图三处写法一致。再检查 hreflang 指向的每个网址是否直接返回 200curl-shttps://example.com/en/pricing\|grep-oElink relalternate hreflang[^] href[^]\|sed-Es/.*href([^])/\1/|sort-u\|whileread-ru;doprintf%s %s\n$(curl-s-o/dev/null-w%{http_code}$u)$u;done200 https://example.com/en/pricing 200 https://example.com/pricing 200 https://example.com/zh/pricing站点地图同理抽查前 25 条只打印不是 200 的curl-shttps://example.com/page-sitemap1.xml\|grep-oEloc[^]/loc|sed-Es#/?loc##g|head-n25\|whileread-ru;doprintf%s %s\n$(curl-s-o/dev/null-w%{http_code}$u)$u;done\|grep-v^200没有输出就是都返回了 200。page-sitemap1.xml是 SEOPress 的文件名换成你的站点地图索引里列出的文件名去掉head可以查整个文件。验证时有两处容易误判。一是缓存站点前面有 CDN 或页面缓存时先清缓存或者在服务器本机用curl --resolve example.com:443:127.0.0.1直连源站再测。TranslatePress 还会把网址转换结果放进对象缓存用了 Redis 这类持久化对象缓存的话过滤器上线之前缓存下来的旧结果可能还在验证前先清一次。二是想直接对比过滤器前后的差异时要把trp缓存分组设成非持久避免读到过滤器上线之前缓存的旧结果两次调用还要用不同的测试网址否则第二次读到的是本次运行里第一次调用缓存下来的结果。我们做对照时第一次就碰到了同一个网址调用两次摘掉过滤器以后输出没有变化换成不同的网址才看到带斜杠的原始输出。下面这个脚本用wp eval-file运行只读?php// check-urls.phpwp_cache_add_non_persistent_groups(array(trp));$converterTRP_Translate_Press::get_trp_instance()-get_component(url_converter);echowith filter: ,$converter-get_url_for_language(en_US,home_url(/test-a),),\n;remove_all_filters(trp_get_url_for_language,999);// 只影响本次运行echoraw: ,$converter-get_url_for_language(en_US,home_url(/test-b),),\n;输出with filter: https://example.com/en/test-a raw: https://example.com/en/test-b/再回到 Search Console对几个核心页面做“URL 检查”里的“测试实际网址”并重新提交站点地图。“已发现 - 尚未编入索引”要等 Google 重新抓取之后才会变改完当天看不到结果这篇记录也不承诺收录会因此恢复能保证的只是站点这一侧的网址信号已经一致。经验先定唯一的规范写法再让所有出口服从它站内链接、canonical、hreflang含 x-default、站点地图、语言切换、重定向目标。结构化数据和og:url里的网址也值得一起查。301 是兜底不是方案。留着它让外部的旧链接继续有效站内链接和站点地图不该依赖它。在服务器层加一条去斜杠的重写规则只能让跳转更快页面和站点地图里写的仍是带斜杠的网址信号不一致的问题还在。例外要写进规则站点根和语言根保留斜杠查询串和锚点原样保留。漏掉语言根会把/en/变成一个会 301 回来的/en。过滤器放在链条末端优先级 999源头也改一遍。只靠过滤器主题里的硬编码链接修不到只改源头第三方插件的输出管不到。把验证命令存成脚本改主题、升级 TranslatePress 或 SEOPress 之后重新跑一遍。上面对 TranslatePress 行为的描述基于我们用的版本不同版本的内部实现可能不一样。斜杠只是“尚未编入索引”众多可能原因中的一个内容质量、站点整体权重、内链数量都要另外排查。这类问题的完整检查流程我们整理成了外贸网站 SEO 优化的服务说明https://wordpress-code.com/foreign-trade-website-seo