微信域名拦截检测原理与PHP实现:从curl探测到批量监控告警 📅 发布时间:2026/9/14 1:57:14 👁 浏览次数: 简介面向微信/QQ推广运营人员、网站站长及PHP开发者的域名拦截检测平台源码用于实时调用检测接口判断站点域名是否被微信或QQ屏蔽一旦发现异常可及时通知负责人从而在短时间内调整推广策略减少封禁带来的流量损失与客户流失。代码采用PHP编写前端基于layui组件库构建整个zip压缩包共112个文件其中JavaScript脚本用于页面交互与接口对接CSS样式表与图标字体负责界面呈现75张GIF动图集中展示操作流程与演示效果资源总体积仅664KB轻量易部署。已有229人学习/下载。资源内不仅包含域名检测调用逻辑、状态查询与异常通知机制还通过清晰的目录模块组织便于定位修改大量GIF动图直观展示平台前后台操作界面与对接效果适合具备一定PHP基础、需要快速搭建域名预警服务或研究微信/QQ屏蔽检测逻辑的中级开发者参考使用。1. PHP微信域名拦截检测所要做的事和源码包里真正有用的部分一个看起来正常的 H5 页面在微信里转发给同事后对方点开看到的是「已停止访问该网页」而不是页面内容这是微信域名拦截最典型的表现。服务器日志里通常还能看到请求确实到达了但微信侧就是不展示页面原因在于微信内置浏览器做了打开前的安全校验而不是等站点慢慢响应。PHP微信域名拦截检测平台要解决的就是把“这个域名在微信侧现在是什么状态”变成一个可定时采集、可查询、可告警的指标通过 PHP 构造与微信客户端一致的校验请求拿到微信侧对链接的判定再把放行、拦截、风险、未知四类结果写入数据库形成历史趋势。源码包里最有价值的部分不是后台模板而是请求构造、返回文案解析、状态落库这条链路的组织方式。这篇主要面向接手同类源码包的 PHP 工程师也适合想从零实现一个轻量微信域名监控服务的人。2. 微信域名拦截检测的探测原理与 PHP 请求构造2.1 检测平台真正要捕获的“拦截结论”微信客户端的链路并不是简单拉起外部 URL。微信内置浏览器在打开网页前会先把 URL 交给安全校验组件命中风险库或存在被大量用户投诉记录时用户看到的是weixin110.qq.com安全提示页。这个安全页本身返回 HTTP 200所以只看状态码完全无法识别“域名被拦截”。检测平台要做的是把目标域名放进微信侧的校验链路中从响应内容里找拦截特征而不是直接 GET 目标域名首页。同一个域名用服务器直连和用微信内置浏览器打开结论可能完全不同。直接 curl 一个未备案站点服务器返回 200 很正常但微信侧会因为域名历史投诉记录而判定风险。反过来目标站点如果对非微信 UA 做了拦截直连检测又会得到错误的失败结果。所以正确思路是构造一个等价于微信客户端“打开链接前校验”的请求再对返回内容做语义识别。常见源码包里探测入口有三种写法直接请求目标站并带上微信 UA把目标地址包进短链服务把目标 URL 拼进mp.weixin.qq.com域的跳转参数里。前两种受目标站自身防护策略影响较大第三种才能在响应里比较稳定地观察到微信安全侧的结论。我一般会直接采用第三种并围绕它做参数可配置。2.2 用 PHP 构建最小微信视角探测请求一个最小可用的实现需要 curl 扩展核心是把请求目标指向mp.weixin.qq.com/mp/redirect把待检测域名放在url参数中并补上微信内置浏览器的 User-Agent 和来源页?php declare(strict_types1); /** * 用微信内置浏览器视角探测一个域名 */ function whx_detect(string $originUrl): array { // 统一补全协议避免 rawurlencode 后拼出不合法地址 if (!preg_match(#^https?://#i, $originUrl)) { $originUrl https:// . $originUrl; } // 让微信侧先对目标链接做安全校验再决定是否跳转 $probeUrl https://mp.weixin.qq.com/mp/redirect?url . rawurlencode($originUrl); $ch curl_init($probeUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER true, CURLOPT_FOLLOWLOCATION true, CURLOPT_MAXREDIRS 5, CURLOPT_TIMEOUT 10, CURLOPT_CONNECTTIMEOUT 5, CURLOPT_SSL_VERIFYPEER false, CURLOPT_SSL_VERIFYHOST false, CURLOPT_USERAGENT Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) . AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 . MicroMessenger/8.0.49(0x18003123) NetType/WIFI Language/zh_CN, CURLOPT_REFERER https://weixin110.qq.com/, CURLOPT_HEADER true, ]); $raw curl_exec($ch); $errno curl_errno($ch); $httpCode curl_getinfo($ch, CURLINFO_HTTP_CODE); $finalUrl curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); curl_close($ch); if ($raw false) { return [ok false, error curl_strerror($errno), errno $errno]; } return [ ok true, http_code $httpCode, final_url $finalUrl, raw $raw, ]; }FOLLOWLOCATION和MAXREDIRS要成对出现否则重定向次数超出限制后curl 会直接返回空内容且不报错。SSL_VERIFYPEER和SSL_VERIFYHOST在纯探测场景下关闭是为了保证拿到微信侧提示页的正文如果这台机器同时还在请求其它业务接口这两个选项要恢复为默认值。超时设成连接 5 秒、总 10 秒是因为被检站点响应慢时会拖住整批任务而慢响应本身不代表拦截结论。2.3 返回内容的拦截状态判定微信安全页文案在不同版本和场景下会有差异判定不能只匹配一个词。把响应正文里的特征关键词与状态做映射特征词或场景判定的状态建议处置出现「已停止访问该网页」blocked停止该域名下的投放检查内容出现「网页包含不安全内容」blocked同上并按流程提交申诉出现「被大量用户投诉」risk提高检测频率进入人工复核最终跳回目标站域名ok维持常规巡检状态码异常且无特征词unknown人工抽查补充关键词后再确认对应的判定函数可以写成这样function judge_block_status(array $probe): array { if (!$probe[ok]) { return [status unknown, reason network_error]; } $body $probe[raw] ?? ; $keywords [ 已停止访问该网页 blocked, 网页包含不安全内容 blocked, 被大量用户投诉 risk, ]; foreach ($keywords as $word $status) { if (strpos($body, $word) ! false) { return [status $status, reason $word]; } } // 未命中安全提示词时看最终是否回到了目标站 $finalUrl $probe[final_url] ?? ; $host parse_url($finalUrl, PHP_URL_HOST); if ($host ! null stripos($host, weixin110.qq.com) false) { return [status ok, reason redirect_back]; } return [status unknown, reason no_keyword]; }关键词表建议放到配置项里而不是写死在代码中因为微信偶尔会调整提示语里的标点和措辞。真机上已经出现拦截页关键词没匹配上时会误报 ok这是最隐蔽的一类问题排错方式我会在最后一章给出。3. PHP 检测平台源码的目录结构与数据模型3.1 源码压缩包中常见的工程布局大多数这类源码包不会携带完整 composer 依赖而是把入口、控制器、服务层和公共函数平铺在几个目录下。拿到压缩包先看根目录通常能发现类似下面的结构domain-check/ ├── app/ │ ├── Controller/ │ │ ├── Admin/IndexController.php │ │ └── Api/CheckController.php │ ├── Service/DetectorService.php │ └── Model/CheckTaskModel.php ├── config/config.php ├── cron/check_domains.php ├── web/index.php └── storage/logs/路径职责我一般会调整的地方app/Controller/Api/CheckController.phpHTTP 入口接收域名并写入待检队列加限流与签名校验app/Service/DetectorService.php探测、重试、状态解析关键词表外置到配置cron/check_domains.phpCLI 批量巡检入口加--limit参数config/config.php数据库、Redis、告警配置改为环境变量覆盖如果源码包里只有单个check.php也不影响理解。控制器的职责是读取请求参数并调用服务层数据处理集中在 ModelCLI 入口只做批量调度。后续要扩展结果回调或趋势报表只需要在网络链路上加两个口子不必重写核心探测逻辑。3.2 任务表与检测记录表的建表语句检测平台最少需要两张表domain_check_task保存“当前该域名处于什么状态”domain_check_log保存每次探测的历史快照。历史记录是为了给状态翻转和申诉处理提供依据也方便回看某次误报是不是微信文案调整引起的。CREATE TABLE domain_check_task ( id int unsigned NOT NULL AUTO_INCREMENT, scheme varchar(8) NOT NULL DEFAULT https, host varchar(190) NOT NULL, status varchar(20) NOT NULL DEFAULT unknown, last_check_at datetime DEFAULT NULL, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_host (host) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci; CREATE TABLE domain_check_log ( id bigint unsigned NOT NULL AUTO_INCREMENT, domain_id int unsigned NOT NULL, check_status varchar(20) NOT NULL, http_code smallint unsigned DEFAULT NULL, final_url varchar(512) DEFAULT NULL, matched_word varchar(128) DEFAULT NULL, cost_ms int unsigned DEFAULT NULL, checked_at datetime NOT NULL, PRIMARY KEY (id), KEY idx_domain_id_checked_at (domain_id, checked_at), KEY idx_check_status (check_status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;host字段加唯一索引可以防止同一域名被不同入口重复插入。任务表的status放当前结论last_check_at用于 cron 里筛选到期未检测的域名。日志表以domain_id checked_at做联合索引是让趋势查询走索引而不是全表扫描check_status单列索引则服务“筛出所有 blocked 域名”的运营场景。3.3 核心检测服务请求、超时与重试策略DetectorService的公开方法只暴露detect()内部把一次探测组织成“发起请求、解析、必要时重试”三步。这样上层调用方不需要关心微信 UA 和重试细节?php declare(strict_types1); require __DIR__ . /../helpers/detect.php; final class DetectorService { private const RETRY_TIMES 2; private const RETRY_DELAY_MS 300; public function detect(int $domainId, string $host, string $scheme https): array { $attempt 0; do { $attempt; $probe whx_detect($scheme . :// . $host); if ($probe[ok]) { $result judge_block_status($probe); if ($result[status] ! unknown) { $result[attempt] $attempt; $result[http_code] $probe[http_code] ?? null; $result[final_url] $probe[final_url] ?? null; return $result; } } usleep(self::RETRY_DELAY_MS * 1000); } while ($attempt self::RETRY_TIMES); return [status unknown, reason max_retry, attempt $attempt]; } }重试次数设 2 次而不是更多是因为微信安全校验偶尔会因为网络抖动返回空白页第 2 次请求大概率能恢复连续 3 次拿不到有效结论时继续重试只会抬高请求频率增加探测入口被临时限流的概率。每次重试间隔固定 300ms属于休眠等待而不是忙等对 CPU 没有压力。4. PHP 批量检测任务的调度、并发与告警实现4.1 批量任务切片与并发控制域名数量到几百个之后串行检测会明显不够用。常见方案是用 Redis 计数做并发闸门限制同时在飞的探测请求数而不是用进程锁把整批任务锁死。function acquire_slot(Redis $redis, string $key, int $limit, int $ttl 120): bool { $pipe $redis-multi(Redis::PIPELINE); $pipe-incr($key); $pipe-expire($key, $ttl); [$count] $pipe-exec(); if ($count $limit) { return true; } $redis-decr($key); return false; } function release_slot(Redis $redis, string $key): void { $redis-decr($key); }计数加过期必须放在同一个 pipeline 里执行否则进程被 kill 后计数不会自动回落任务会一直拿不到槽位。ttl设 120 秒表示槽位在超时后自动释放正常完成的探测在结束时调用release_slot把计数减回去。批量循环里每处理一个域名前先acquire_slot阈值设 2 个并发就够用超过阈值时当前进程 sleep 500ms 再重试获取不要直接丢弃任务。4.2 定时调度与 PHP CLI 入口检测请求里带微信 UA 并且集中在一个时间段发起容易触发链路限制所以平台一般放在 crontab 里按 5 分钟或 10 分钟跑一次避免 Web 请求处理耗时检测时占满 PHP-FPM 进程。crontab 里通常写成*/5 * * * * /usr/bin/php /data/www/domain-check/cron/check_domains.php --limit100 /data/www/domain-check/storage/logs/cron.log 21--limit100表示每轮最多处理 100 个域名。CLI 脚本拿到参数后从任务表筛出待检域名?php declare(strict_types1); $opts getopt(, [limit::, host::]); $limit (int) ($opts[limit] ?? 50); $sql SELECT * FROM domain_check_task WHERE last_check_at IS NULL OR last_check_at NOW() - INTERVAL 30 MINUTE ORDER BY last_check_at IS NULL DESC, last_check_at ASC LIMIT . (int) $limit;这个查询会把从未检测的域名排在最前之后每 30 分钟重新检测一次。LIMIT 直接拼接数字不会引入注入因为$limit已经被(int)强转如果项目规范要求全参数化也可以把 LIMIT 挪进bindValue。CLI 脚本内部要捕获SIGTERM方便部署时用 Supervisor 平滑重启。4.3 状态翻转检测与企业微信机器人告警只记录状态还不够运营需要的是“域名状态发生变化”时能立刻收到通知。做法是每次插入检测日志后与上一次日志的状态做对比$lastLog $logModel-getLatest($domainId); if ($lastLog ! null $lastLog[check_status] ! $result[status]) { pushWecom(WECOM_WEBHOOK_URL, 域名 {$host} 微信拦截状态变化{$lastLog[check_status]} - {$result[status]}命中词{$result[reason]}); }企业微信群机器人是成本最低的通知通道在群里添加机器人拿到 webhook 地址后用下面的函数推送文本消息function pushWecom(string $webhookUrl, string $content): bool { $payload json_encode([ msgtype text, text [content $content], ], JSON_UNESCAPED_UNICODE); $ch curl_init($webhookUrl); curl_setopt_array($ch, [ CURLOPT_POST true, CURLOPT_POSTFIELDS $payload, CURLOPT_RETURNTRANSFER true, CURLOPT_TIMEOUT 5, CURLOPT_HTTPHEADER [Content-Type: application/json], ]); $body curl_exec($ch); curl_close($ch); $data json_decode((string) $body, true); return ($data[errcode] ?? -1) 0; }正常情况下企业微信群机器人接口返回errcode0如果返回 93000 这类错误表示 webhook 地址失效或机器人被移出群此时要把原始返回体写入日志。注意不要把检测告警和企业微信 Linux 客户端里的其它群机器人脚本共用同一个 webhook群机器人的限流额度是一份混用会在高峰时段互相挤掉配额。提示上线 cron 之前先手动跑一次php cron/check_domains.php --hostexample.com确认能写出第一条检测日志再交给定时器。5. 微信域名拦截检测平台的真机验证与调优5.1 部署时的最小步骤与验证顺序依赖主要是 PHP 8.x 的 curl、pdo_mysql、redis 扩展。顺序是建库、导入两张表、改config/config.php里的数据库连接和 webhook 地址然后手动执行一次 CLI。第一次返回 unknown 不要急着怀疑代码先开 DEBUG 打印原始响应正文确认关键词表是否需要补充。平台跑通后再挂到 crontab不要一开始就把几百个域名全量灌进去。5.2 用真机校准一次检测结论准备两个域名一个明显正常的内容站一个疑似被拦截的落地页用微信“文件传输助手”分别发送给任意联系人。真机上能看到真实弹窗把平台返回和真机结果对齐都判定 blocked 说明当前关键词表有效出现偏差就更新配置里的关键词表再触发一次检测不要改代码重新部署。这个动作应该写进平台的上线检查清单因为微信策略调整没有公开时间表只能靠真机结果反向校准。5.3 三个最常踩的坑第一超时参数不能沿用 curl 默认值批量任务到 200 个域名以上时单请求 30 秒超时会把整个 cron 拖到无法收尾。第二UA 里的MicroMessenger版本号要跟着新版微信走旧版本 UA 在某些新校验分支下拿不到完整提示文案。第三先写日志表再更新任务表顺序反了会丢失状态变化前最后一次快照状态翻转告警也就无从对比。5.4 给运营一份可读的“被拦截域名清单”检测数据最终要落到业务动作上。每天定时导出被拦截域名列表推给运营和客服可以直接用一条 SQL 关联任务表和日志表SELECT d.host, d.status, l.matched_word, l.checked_at FROM domain_check_task d LEFT JOIN domain_check_log l ON l.id ( SELECT id FROM domain_check_log WHERE domain_id d.id ORDER BY checked_at DESC LIMIT 1 ) WHERE d.status blocked ORDER BY l.checked_at DESC;拿到列表后可以拼成 Markdown 表格推到企业微信或者导出 CSV 供客服回访。到这一步整个 PHP 微信域名拦截检测平台已经具备检测、存储、告警、复核的闭环后续真正需要持续维护的只有关键词表、检测频率和微信用 UA 版本这三处配置项。本文还有配套的精品资源点击获取