前言:新站最痛的不是写得差,是百度压根不知道你写了
很多人做站的第一反应是「内容不够好」,于是拼命改标题、加关键词、调排版。但真实情况往往是另一回事:文章早就发布了,搜索引擎根本没来过。
尤其是新站、或者更新不频繁的站,蜘蛛来的间隔可能是几天甚至几周。你写了一篇自认为不错的教程,它在你自己的服务器上躺了半个月,百度索引里查不到,搜索域名全称都搜不出来。
解决这件事有三条路,成本从低到高,效果也从弱到强。
一、三条路:sitemap、主动推送、IndexNow
| 方式 | 原理 | 生效速度 | 成本 |
|---|---|---|---|
| sitemap | 把全站链接列成一个 XML,等蜘蛛自己来取 | 慢,取决于蜘蛛来访频率 | 最低,插件自动生成 |
| 百度主动推送 | 文章一发布,立刻用 API 把这条 URL 推给百度 | 快,通常几分钟到几小时 | 中,需要一个 token |
| IndexNow | 推一次,同时通知 Bing、Yandex、Seznam 等多个引擎 | 最快,Bing 常常秒收 | 低,只需一个 key 文件 |
三者不冲突,建议同时开:sitemap 兜底,主动推送解决百度,IndexNow 解决 Bing 系。
二、百度主动推送:从申请 token 到第一次推送
1. 拿到 token
进百度站长平台 → 找到你的站点 → 资源提交 → 主动推送 → 接口调用地址,长这样:
http://data.zz.baidu.com/urls?site=www.511yj.com&token=你的token
这个地址里 site 是域名,token 是你的密钥。token 不要写进公开文章或提交到代码仓库。
2. 接口格式(极简单,但有两个硬性要求)
<code>POST /urls?site=www.511yj.com&token=xxx HTTP/1.1 Content-Type: text/plain https://www.511yj.com/a.html https://www.511yj.com/b.html</code>
两个容易踩的点:
- 请求体是纯文本,一行一个 URL,不是 JSON
- Content-Type 必须是
text/plain,用 JSON 提交会返回「数据格式错误」
返回里 success 是本次成功条数,remain 是当天剩余配额。新站配额一般够用,不用太担心。同一个 URL 当天重复推,只算一次。
3. 挂到发布动作上
<code>add_action('transition_post_status', function($new, $old, $post){ if ($new !== 'publish' || $old === 'publish') return; if ($post->post_type !== 'post') return; $api = 'http://data.zz.baidu.com/urls?site=www.511yj.com&token=xxx'; $body = get_permalink($post->ID); $res = wp_remote_post($api, [ 'timeout' => 15, 'headers' => ['Content-Type' => 'text/plain'], 'body' => $body, ]); if (!is_wp_error($res)) { update_post_meta($post->ID, '_baidu_push_result', wp_remote_retrieve_body($res)); } }, 10, 3);</code>
用 transition_post_status 而不是 save_post,是因为它只在状态真正变成 publish 时触发,草稿保存、自动保存都不会误推。
三、最阴的一种失败:curl 没加载,推送「成功」其实是假的
这一段是整篇文章最值得看的部分,因为它没有任何报错。
WordPress 的 wp_remote_post() 是个双通道设计:curl 扩展可用就走 curl,不可用就降级到 WP_Http_Streams。听起来很稳,但在某些服务器上,stream 通道会因为 allow_url_fopen 关闭或 SSL 配置缺失而静默失败——请求根本没发出去,函数照样返回一个结构完整的结果。
于是你在后台看到「推送完成」,日志里干干净净,百度那边一条都没收到。
先确认一件事,在服务器上跑:
<code>C:\xampp\php\php.exe -r "var_dump(extension_loaded('curl'));"</code>
输出 bool(false) 的话,先去把 curl 修好再谈推送。
相关阅读:Windows Server 2012 R2 下 XAMPP PHP 8.3 的 curl 扩展修复实录——那篇讲了 DLL 依赖链断裂导致扩展静默不加载的完整排查过程,是本文这个坑的前置条件。
推送代码里也可以加一道保险,把失败显性化:
<code>if (is_wp_error($res)) { error_log('[baidu-push] ' . $res->get_error_message()); return; } $code = wp_remote_retrieve_response_code($res); if ($code !== 200) { error_log('[baidu-push] HTTP ' . $code . ' ' . wp_remote_retrieve_body($res)); }</code>
四、修好 curl 反而报错:error 60 到底是谁的锅
很多人是在这一步放弃的:按教程下载了 cacert.pem,配好了 curl.cainfo,重启,然后推送报:
cURL error 60: SSL certificate problem:
unable to get local issuer certificate
第一反应通常是「证书包不对」或者「我配错了」,于是换证书、改路径、甚至直接关掉校验。但在动手之前,应该先做一个判断:到底是你这边的问题,还是对方的问题。
用「对照组」定位,别猜
做法很简单:拿同一个证书包,去访问另外两个确定正常的 HTTPS 站点。
<code>add_action('admin_init', function(){ if (!isset($_GET['ssl-probe'])) return; $targets = [ '微信 API' => 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential', '公网 IP' => 'https://api.ipify.org', '百度推送' => 'http://data.zz.baidu.com/urls?site=www.511yj.com&token=xxx', ]; foreach ($targets as $name => $url) { $r = wp_remote_get($url, ['timeout' => 15]); echo $name . ' => ' . (is_wp_error($r) ? $r->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($r)) . '<br>'; } exit; });</code>
结果怎么读:
| 现象 | 结论 |
|---|---|
| 三个全失败 | 你的问题——证书包路径错、文件损坏,或 curl 没加载 |
| 微信和 IP 成功,只有百度失败 | 百度端的问题——它的证书链/主机名配置有问题,跟你的证书包无关 |
第二种情况真实存在。data.zz.baidu.com 在某些网络环境下返回的证书,其主机名与访问域名不匹配,而 PHP 的 curl 在严格校验模式下会直接拒绝。浏览器能打开是因为浏览器有额外的容错和补全机制,服务器端的 curl 没有。
原则是:可以降级,但不能静默
遇到对方证书有问题,正确做法不是全局关掉校验——那会把所有 HTTPS 请求都暴露在风险里,而且将来你自己的站点真出问题也发现不了。
应该只在确认是 SSL/证书类错误时才跳过校验,并且明确标记:
<code>$res = wp_remote_post($api, $args); if (is_wp_error($res)) { $msg = $res->get_error_message(); // 只有 SSL 类错误才降级,403/429 等绝不能降级 if (stripos($msg, 'SSL') !== false || stripos($msg, 'certificate') !== false) { $args['sslverify'] = false; $res = wp_remote_post($api, $args); if (!is_wp_error($res)) { $res['_noverify'] = true; // 必须留痕 } } }</code>
关键点:403(token 错)、429(配额超限)这类错误绝对不能降级——它们是真实的业务错误,跳过校验只会让你误以为推成功了。
五、IndexNow:一次推送,多个引擎
IndexNow 是微软和 Yandex 推的开放协议,逻辑极简单:你告诉任何一个参与的搜索引擎「这个 URL 变了」,它会把消息广播给其他参与者。目前 Bing、Yandex、Seznam、Naver 都支持。
1. 生成 key 并放成文件
key 是你自己随便定的一串字符,要求是 8-128 位的十六进制字符(a-f, 0-9)。比如可以用随机生成:
$key = bin2hex(random_bytes(16)); // 32 位,合法
然后在网站根目录放一个纯文本文件 key.txt,内容就只有这一串 key,不能有多余空格或换行。
验证:https://www.511yj.com/key.txt 应该直接显示那串字符。
2. 推送请求
<code>{ "host": "www.511yj.com", "key": "你的key", "keyLocation": "https://www.511yj.com/key.txt", "urlList": [ "https://www.511yj.com/a.html", "https://www.511yj.com/b.html" ] }</code>
$res = wp_remote_post('https://api.indexnow.org/indexnow', [
'timeout' => 20,
'headers' => ['Content-Type' => 'application/json; charset=utf-8'],
'body' => wp_json_encode($payload),
]);
3. 403 的头号原因:两处 key 对不上
IndexNow 返回 403,九成是因为服务器上的 key.txt 内容,和你在插件里填的、请求里发的 key 不一致。
这不是什么玄学。常见成因:
- 改过 key,但忘了重新上传
key.txt key.txt尾部多了个换行(很多编辑器默认会加)- 插件设置里填的时候复制漏了字符
所以自检功能必须有,而且必须是真的去 HTTP 请求一次并比对内容,不是检查文件存不存在:
<code>$r = wp_remote_get($key_location, ['timeout' => 15]); $code = wp_remote_retrieve_response_code($r); $body = trim(wp_remote_retrieve_body($r)); if ($code !== 200) { return 'key.txt 无法访问(HTTP ' . $code . ')'; } if ($body !== $key) { return 'key.txt 内容与设置不一致:文件里是 ' . $body . ',设置里是 ' . $key; } return 'key 验证通过';</code>
六、一个能长期用的推送插件,该有什么设计
把代码挂到 transition_post_status 上只是起点。真要长期用,下面这些是必需的。
| 设计点 | 为什么需要 |
|---|---|
| 推送日志 | 没有日志就只能靠「猜」。记录 URL、时间、HTTP 码、返回内容 |
| 失败重试 | 网络抖动很常见,一次失败不该就此丢掉 |
| SSL 失败记忆 | 确认校验不过的机器,12 小时内直接走降级,省掉注定失败的握手 |
| 跳过密码保护文章 | 搜索引擎抓不到,推了没意义 |
| 日志自动清理 | 批量推一次能写几千行,不清理会撑大数据库 |
| 删除文章时清日志 | 不留指向已不存在页面的记录 |
| 超时设 20 秒 | 发布文章时不能干等,超过就放弃本次 |
尤其「日志」这一项,没有它,前面所有问题你都发现不了。
七、避坑清单
| 坑 | 说明 |
|---|---|
| curl 没加载就配推送 | 顺序反了。此时推送可能静默失败,先把扩展修好 |
| 用 JSON 提交百度接口 | 必须 text/plain,一行一个 URL |
| token 写进公开代码 | 等于把推送权限交出去 |
| 遇到 error 60 就全局关校验 | 先做对照组判断是谁的问题,降级也要留痕 |
| 403 也去降级 | 403 是真实的业务错误,降级只会掩盖问题 |
| key.txt 多了换行 | IndexNow 403 的头号成因。用 trim 比对 |
| 改了 key 没换文件 | 两处必须完全一致 |
| 没有日志 | 出问题只能靠猜,无法回溯 |
八、怎么确认真的推成功了
三个层次的验证,从快到慢:
- 看接口返回:百度返回
success:1,IndexNow 返回 200,这一步最及时 - 看插件日志:确认 URL、时间、状态码都记下来了,不是空白
- 搜索引擎验证:百度站长平台查「索引量」,Bing 用
site:域名搜索,通常需要几小时到几天
如果前两步都正常、第三步几天后仍无变化,那问题就不在推送了——通常是内容质量、站点权重或 robots 配置的问题,得换方向查。
写在最后
主动推送这件事,技术上不复杂,难的是它失败的时候经常不报错。curl 没加载会静默失败,stream 通道不通会静默失败,对方证书有问题会让你误以为是自己配错了。
所以真正值钱的不是那几行推送代码,而是配套的日志、对照组判断和失败留痕。有了这些,出问题你能在五分钟内定位;没有,就只能像本文第三节那样,白白等上两周才发现一条都没推成功。