本文约 4,893 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:你链着他,他早把你删了
友链交换里最常发生却最少被发现的事,是「单链」。对方换了模板、改了友链页、清理了一批链接,你的链接在他那边已经消失,而你这边依然把他的站点挂在页脚。这种情况通常能持续几个月甚至几年,直到某天你手动点开友链页才发现。单链的损失有两层:一是你把宝贵的出站位置与信用背书给了一个不再回馈的站点;二是如果对方站点后来变质(变成采集站或灰产站),你还在替他背书,风险完全由你承担。靠人工逐个点开检查,二十个链接要花十分钟,三十个链接基本就没人愿意做了,所以必须自动化。
原理分析:为什么必须用服务端定时检查
有人会说「装个友链插件就能看到」。确实,WordPress 目录里有 Link Manager(核心在 3.5 后被默认隐藏,可用 add_filter( 'pre_option_link_manager_enabled', '__return_true' ) 重新开启)以及各类友链插件,但它们只管理「你这边的链接列表」,不会去验证对方是否还有你的链接。真正的巡检必须主动去抓对方的页面并在返回内容里搜索自己的域名,这是一个服务端动作,需要三样东西:定时调度、HTTP 请求、结果记录。WordPress 提供了完整的组合:wp_schedule_event 负责调度,wp_remote_get 负责请求,结果存进 wp_options 或自定义表。
| 检查项 | 判定方式 | 处理动作 |
|---|---|---|
| 对方页面可访问 | HTTP 200 | 否则标记待人工确认 |
| 页面含本站域名 | 字符串匹配 | 否则标记「疑似被撤」 |
| 链接带 nofollow | rel 属性检查 | 记入备注,可协商 |
| 对方站点跳转异常 | 301 到无关域名 | 立即下架 |
| 连续多次失败 | 计数阈值 | 自动下架或告警 |
注意 WP-Cron 的触发机制:它靠站点访问触发,低流量站点可能不准时。生产环境建议把系统 crontab 指向 wp-cron.php,并禁用访问触发。
保姆级实操
步骤 1:把系统 crontab 接上 WP-Cron。 先在 wp-config.php 里禁用访问触发,再用系统定时执行:
# 每 10 分钟触发一次 WordPress 定时任务
*/10 * * * * /usr/bin/php /var/www/html/wp-cron.php > /dev/null 2>&1
步骤 2:用一个数组维护友链清单。 可以写在主题配置里,也可以用 Link Manager 的书签分类。下面示例用一个可过滤的数组,方便后续扩展成后台配置项:
function mmf_friend_links() {
return apply_filters( 'mmf_friend_links', [
[ 'name' => '示例站点 A', 'check_url' => 'https://a-example.com/links/', 'home' => 'https://a-example.com' ],
[ 'name' => '示例站点 B', 'check_url' => 'https://b-example.com/friends.html', 'home' => 'https://b-example.com' ],
] );
}
步骤 3:注册每日巡检任务。 注意 wp_schedule_event 要在任务未注册时才注册,重复注册会堆积:
add_action( 'init', function () {
if ( ! wp_next_scheduled( 'mmf_friend_link_check' ) ) {
wp_schedule_event( time(), 'daily', 'mmf_friend_link_check' );
}
} );
add_action( 'mmf_friend_link_check', 'mmf_run_friend_link_check' );
function mmf_run_friend_link_check() {
$host = wp_parse_url( home_url(), PHP_URL_HOST );
$links = mmf_friend_links();
$report = [];
foreach ( $links as $link ) {
$response = wp_remote_get( $link['check_url'], [
'timeout' => 15,
'redirection' => 3,
'user-agent' => 'Mozilla/5.0 (compatible; LinkChecker/1.0)',
'sslverify' => true,
] );
if ( is_wp_error( $response ) ) {
$report[] = [
'name' => $link['name'],
'status' => 'error',
'note' => $response->get_error_message(),
];
continue;
}
$code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
$report[] = [
'name' => $link['name'],
'status' => 200 === (int) $code ? 'ok' : 'http_' . $code,
'has_link' => ( false !== strpos( $body, $host ) ) ? 'yes' : 'no',
'nofollow' => (bool) preg_match( '/<a[^>]+' . preg_quote( $host, '/' ) . '[^>]*rel="[^"]*nofollow/i', $body ),
'checked_at'=> current_time( 'mysql' ),
];
}
update_option( 'mmf_friend_link_report', $report, false );
mmf_notify_if_removed( $report );
}
步骤 4:发现异常就通知。 只在状态变化时才发邮件,避免每天都收到重复通知:
function mmf_notify_if_removed( $report ) {
$bad = array_filter( $report, function ( $item ) {
return 'ok' !== $item['status'] || ( isset( $item['has_link'] ) && 'no' === $item['has_link'] );
} );
if ( empty( $bad ) ) {
delete_option( 'mmf_friend_link_alerted' );
return;
}
// 同一批问题只提醒一次,避免每日重复邮件
$fingerprint = md5( wp_json_encode( array_column( $bad, 'name' ) ) );
if ( get_option( 'mmf_friend_link_alerted' ) === $fingerprint ) {
return;
}
update_option( 'mmf_friend_link_alerted', $fingerprint, false );
$lines = [ "以下友链巡检异常,请人工确认:", '' ];
foreach ( $bad as $item ) {
$lines[] = sprintf(
'- %s:状态 %s,是否含本站 %s',
$item['name'],
$item['status'],
$item['has_link'] ?? '未知'
);
}
wp_mail(
get_option( 'admin_email' ),
'[' . get_bloginfo( 'name' ) . '] 友链巡检异常提醒',
implode( "\n", $lines )
);
}
步骤 5:人工确认后再下架。 脚本只做提醒,不做自动删除——有些站点临时 502、或者友链页改版正在调整,误删会得罪人。确认对方确实撤了,再在自己这边移除,并把这条记录写进台账。
步骤 6:给巡检结果做一个后台展示。 把 mmf_friend_link_report 读出来渲染成一个简单的管理页面,比每次翻数据库方便。也可以直接把它输出在页脚注释里,便于快速查看。
常见踩坑
- 巡检频率设成每小时,三十个站点每次都要抓取,被对方当成攻击封了 IP。
- 案例: 某站把巡检脚本写成发现异常立即自动删除对方链接,结果对方站点临时维护返回 503,脚本当天删掉了 12 个正常友链,站长花了两周才逐个重新谈回来。
- WP-Cron 未接系统 crontab,低流量站点任务几天才跑一次,巡检形同虚设。
- 用
file_get_contents而不是wp_remote_get,对方站点没开allow_url_fopen时直接报错。 - 只检查首页,对方把友链放在专门的 links 页面,首页当然没有你的链接。
- 匹配域名时没考虑
www与裸域差异,导致明明有链接却判定为被撤。 - 忽略了对方站点被攻击后植入的跳转代码,HTTP 仍是 200,但实际已 302 到无关站点。
总结
友链巡检的判断标准很简单:连续两次巡检显示对方页面不含你的域名,或页面跳转到了无关站点,就该联系确认或直接下架。行动清单:接系统 crontab → 维护友链清单 → 注册每日任务抓页面并匹配域名 → 异常去重提醒 → 人工确认后下架 → 台账记录。巡检的价值不只是防单链,更是发现对方站点变质的第一道哨卡。友链挑选的标准见 百度友情链接交换原则,发现对方站点出问题后的处理见 被友链牵连降权的排查。
