我在博客上挂了「发文章自动推送百度」这个功能,一直以为它在工作。
直到那天我心血来潮,直接去调了一次百度接口——报错。
回头查服务器上的配置,才发现问题从第一天起就存在。
先说这个功能值不值得做
百度发现新页面有三条路:等蜘蛛自己爬、提交 sitemap、主动推送。
前两条都是「等」—— sitemap 要百度定期来拉,蜘蛛爬更看它心情。主动推送是唯一能「发完立刻告诉百度」的。
对老站意义不大,蜘蛛本来就来得勤。但新站完全不一样:你没权重,蜘蛛来得稀,推送相当于主动把「我这儿有新内容」递到百度面前。
值得做。而且配好之后是全自动的,不用管。
一个对照实验,把根因钉死
百度「普通收录」的推送接口长这样:
http://data.zz.baidu.com/urls?site=你的站点&token=你的token
我先猜了三种可能,逐个排除:
token 过期? 去站长平台重新复制了一遍,还是报错。
配额用尽? 但配额用尽报的是 over quota,跟我这个不是一回事。
站点没验证? 重新确认了验证状态,是正常的。
一个都不对。
后来我做了个最笨的对照实验,只改一个参数:
# 带协议
curl -H 'Content-Type:text/plain' \
--data 'https://www.example.com/post.html' \
'http://data.zz.baidu.com/urls?site=https://www.example.com&token=xxx'
# {"error":400,"message":"site init fail"}
# 不带协议
curl -H 'Content-Type:text/plain' \
--data 'https://www.example.com/post.html' \
'http://data.zz.baidu.com/urls?site=www.example.com&token=xxx'
# {"remain":9,"success":1}
根因就一个:site 参数不能带协议。
写 https://www.example.com 报 site init fail,写 www.example.com 才正常。我把线上配置翻出来一看,存的就是带协议的那一串。

为什么半年没人发现
这才是更值得说的部分。
问题不只在参数写错,还在于旧代码根本不看结果:
wp_remote_post( $api, array(
'timeout' => 3,
'blocking' => false, // ← 元凶在这
'body' => $url,
) );
blocking => false 的意思是:请求发出去就不管了,不等待响应。
好处是发布文章时不会因为百度接口慢而卡住页面;坏处是——推送失败你永远不会知道。
参数错误 + 静默失败,两件事叠在一起,就成了「功能看起来配好了,实际上半年没成功过一次,而没有任何人察觉」。
正确的写法
修的时候顺手加了三道保险。这是现在在跑的代码:
/** site 参数必须剥掉协议 —— 带协议会 site init fail */
function baidu_host() {
$s = trim( (string) BAIDU_SITE );
$s = preg_replace( '#^https?://#i', '', $s );
return rtrim( $s, '/' );
}
function baidu_post( $url ) {
$api = 'http://data.zz.baidu.com/urls?site=' . rawurlencode( baidu_host() )
. '&token=' . rawurlencode( BAIDU_TOKEN );
$r = wp_remote_post( $api, array(
'timeout' => 8,
'blocking' => true, // ① 拿响应
'headers' => array( 'Content-Type' => 'text/plain' ),
'body' => $url,
) );
if ( is_wp_error( $r ) ) {
return array( false, '网络错误:' . $r->get_error_message(), null );
}
$code = (int) wp_remote_retrieve_response_code( $r );
$body = json_decode( wp_remote_retrieve_body( $r ), true );
if ( 200 === $code && isset( $body['success'] ) ) {
return array( true, '成功', (int) $body['remain'] );
}
return array( false, $body['message'] ?? ( 'HTTP ' . $code ), null );
}
三道保险分别是:
| # | 保险 | 解决什么 |
|---|---|---|
| ① | blocking => true |
拿得到响应,不再「发了不知道结果」 |
| ② | 结果写进 postmeta | 后台文章列表直接显示推送状态 |
| ③ | 失败自动重试 | 配额用尽这类临时错误,1/2/3 小时后各重试一次 |
第 ③ 条用 wp_schedule_single_event 排个延迟任务,不用你手动补推。

顺便说下配额
普通收录的额度是百度按站点质量动态给的,新站一般每天 10 条左右。我配好那天推了两次,剩余从 10 掉到 8。
所以代码里还加了去重:同一个 URL 6 小时内不重复推。不然你在编辑器里改五次错别字,一天的额度就没了。
怎么确认它真的在工作
配好之后不要只信代码,去验证。三种办法,从简单到彻底:
① 看后台。加了状态列之后,文章列表里能直接看到:未推送 / ✓ 成功(剩余配额)/ ✗ 失败(原因)。
② 直接调一次接口:
// 在服务器上跑,把 123 换成你的文章 ID
php -r 'define("WP_USE_THEMES", false);
require "/path/to/wp-load.php";
var_dump( baidu_post( get_permalink(123) ) );'
③ 查 hook 有没有注册上:
var_dump( has_action('publish_post', 'baidu_push') ); // 返回 10 就对了
第三种最容易被忽略——代码写对了,不代表 hook 挂上了。
最后
回头看,最坑的地方其实不是那个参数。
是 blocking => false。
它当时是个"合理"的选择:发布文章不该被第三方接口拖慢。但它把"失败"这个信息一起吞掉了。
现在我给自己定了个规矩:凡是调外部接口,都要拿响应、记结果、能看见。 哪怕只记一行日志。
慢几秒可以忍,不知道它死没死,忍不了。
本站为个人技术记录,内容均为原创实操总结,转载请注明出处。