网站可用性与证书巡检
做什么、不做什么
做什么
- 用户说「帮我看看网站挂没挂 / 证书快过期没 / 最近是不是变慢了」。
- 用户手上有好几个站(官网、后台、活动页、API 入口),想一次性巡检、留历史、出可用率。
- 每次检查这些项:
- HTTP 状态:和清单里的期望状态码比对(默认 200,可以写成列表,比如
[200, 302])。 - 跳转链:默认跟随 301/302/303/307/308,最多 10 跳,成环会报出来;也可以
follow_redirects: false只看第一跳(用来确认「旧域名确实 301 走了」)。跳转只跟到清单里出现过的主机或它们的子域(比如example.com→www.example.com);跳到别的域名会停下并在报告里写明,如果那也是用户自己的站,在这一项里加"redirect_hosts": ["login.example.net"]即可。 - 响应耗时:拆成 DNS / TCP 连接 / TLS 握手 / 首字节 / 总耗时;有跳转时各段是所有跳的合计。
- 页面关键词:清单里写了
keyword的,检查最终页面里还在不在(防止被替换成错误页、空白页、被篡改)。页面解压后超过 5 MiB、压缩内容解不开、传输中断、返回的不是文本时,找不到关键词不算「丢失」也不算正常,单独报「关键词无法判断」。 - TLS 证书:证书有效时给出剩余天数、到期时间、签发机构;证书校验没通过时,直接写明原因(已过期 / 域名不匹配 / 自签名 / 证书链不完整等)和下一步该怎么处理,不再继续访问这个站。
- 解析 IP:本机解析到的地址和实际连上的地址。
- 每次结果带时间戳追加写进 SQLite(
history.db),history子命令可以汇总近 N 天的可用率、平均/P95 耗时、故障时段。
不做什么
- 不做压测、不做高频刷接口:一次 run 对每个站只发一个 GET(加上跳转),默认并发 8、最多 16,同一主机同时最多 2 个请求;清单里同一网址写了多次会在报告「提示」里告警。
- 不执行 JavaScript、不渲染页面:纯前端渲染的页面、靠 meta refresh / JS 跳转的页面,关键词检查可能不准,报告里会提示。
- 不登录、不带 Cookie、不提交表单,只看公开页面。
- 不修改任何站点配置,也不替用户续证书、改 DNS。
- 不发通知:告警推到哪(邮件、群机器人、短信)由定时任务或用户自己的工具接,脚本只负责给出结果和退出码。
会访问哪里、会写什么(先跟用户说清楚)
- 只访问用户自己清单里写的网址。脚本里没有写死任何外部地址,不上报数据,不连任何第三方服务;清单为空就一个站都不访问。第一次跑之前,把清单里的网址念给用户确认一遍。
- 每个站只发普通的 GET 请求,不带账号、Cookie、密码或密钥,不提交表单。
- 默认不碰内网:每一跳都先解析主机,地址落在本机、内网、链路本地或云服务器元数据段(127/8、10/8、172.16/12、192.168/16、169.254/16、0.0.0.0、::1、fc00::/7 等),或主机名是
localhost、*.local、*.internal这类内网名,就不发请求,报告里写明原因。巡检自家内网服务时,由用户显式加--allow-private。直连时只连接检查过的那几个地址。 - 代理:只有用户给了
--proxy,或本机已经设了代理环境变量(HTTPS_PROXY/HTTP_PROXY/ALL_PROXY,并遵守NO_PROXY)时才走代理;主机名和端口会先规范化校验,含换行、空格、@等字符的直接拒绝。除这几个代理变量外,只读系统的用户目录位置(用来找「文档\网站巡检」),不读其他环境变量。 - 证书校验始终开启:每次连接都按系统信任的证书机构校验证书和域名,任何情况下都不会关闭校验。校验没通过就在握手阶段停下,报告里写「证书校验没通过:原因」并给出下一步建议,不会绕过校验再去读页面或证书内容。
- 本机写入只有三处:历史库
history.db(默认在sites.json同目录,只追加记录)、--out指定的 Markdown 报告、--json指定的 JSON 报告。 - 位置限制:这三个文件都必须在当前工作目录或用户的「文档\网站巡检」文件夹里(按真实路径判断,快捷方式/链接绕不过去),否则直接拒绝;确实要写到别处,由用户显式加
--allow-outside,报告「提示」里会写明写到了哪。 - 不覆盖:报告文件已存在时默认另存为
report_1.md、report_2.md……,不动旧文件;加--force才覆盖。写入时先写同目录的临时文件再整体替换,写失败会清掉自己刚建的临时文件,不会留下半截报告。 - 不写错文件:
--out/--json和sites.json、历史库是同一个文件,或两者互相相同,直接拒绝。--db指向的文件已存在时,必须是本工具建的 SQLite 历史库(有本工具的表结构和版本标记),否则拒绝、一个字节都不写;0.1.2 及以前建的历史库会自动补上版本标记。 - 除上面这些外,不删除任何文件,不改系统设置,不设定时任务;
--no-save时不写历史库。 - 要定时跑,由用户自己在系统或智能助手里设置,下面「配合定时任务」只是示例。
使用流程
要求 Python ≥ 3.8(python --version 确认),只用标准库,Windows / macOS / Linux 通用。
1. 准备站点清单
把 {baseDir}/assets/sites.example.json 复制一份到用户自己的工作目录,改名 sites.json,照着改:
{
"defaults": { "timeout": 10, "slow_ms": 3000, "cert_warn_days": 14, "follow_redirects": true },
"sites": [
{ "name": "官网首页", "url": "https://www.example.com/", "expect_status": 200, "keyword": "Example Domain" },
{ "name": "后台登录页", "url": "https://admin.example.com/login", "expect_status": [200, 302], "keyword": ["登录", "密码"], "timeout": 15 },
{ "name": "旧域名跳转", "url": "http://example.com/", "expect_status": 301, "follow_redirects": false },
"https://blog.example.com/"
]
}
- 每项可以只写一个 URL 字符串,其余用默认值。
keyword选页面上稳定不变的字(站名、备案号、页脚公司名),别选会随活动变的文案。- 同一个 URL 可以用不同名称、不同规则出现多次,历史按「名称 + URL」分开统计。
- 历史库默认放在
sites.json同目录的history.db,所以不要把sites.json放在技能目录里。
2. 跑一轮检查
python {baseDir}/scripts/check.py run --sites sites.json
python {baseDir}/scripts/check.py run --sites sites.json --out report.md --json report.json
python {baseDir}/scripts/check.py run --sites sites.json --json - # JSON 打到屏幕
python {baseDir}/scripts/check.py run --sites sites.json --only 官网 --no-save
常用参数:
| 参数 | 作用 |
|---|---|
--timeout 10 |
每一跳的超时秒数(默认 10) |
--workers 8 |
并发数(默认 8,最多 16;同一主机同时最多 2 个请求) |
--slow-ms 3000 |
总耗时超过它记为「变慢」(默认 3000) |
--cert-warn-days 14 |
证书剩余天数 ≤ 它就预警(默认 14) |
--proxy http://127.0.0.1:7890 |
走 HTTP 代理(CONNECT 隧道);不给就读 HTTPS_PROXY / HTTP_PROXY 环境变量,遵守 NO_PROXY |
--no-proxy |
忽略所有代理设置强制直连 |
--only 关键字 |
只检查名称或 URL 含该字的站点 |
--no-save |
只看结果不写库(临时排查用) |
--db 路径 |
指定历史库位置(已存在的必须是本工具的历史库) |
--force |
--out / --json 已存在时覆盖(默认另存为 _1、_2…) |
--allow-private |
允许访问内网/本机地址(默认拒绝,巡检自家内网服务时才加) |
--allow-outside |
允许把报告和历史库写到当前目录、「文档\网站巡检」以外(默认拒绝) |
退出码:0 全部正常;1 有警告(证书将到期、变慢、关键词丢失、关键词无法判断);2 有严重问题(宕机、证书校验没通过)或参数错误。参数或清单有误时,屏幕上会给两行提示:「出错了:…」和「下一步:…」。
变慢的判定:总耗时超过 slow_ms;或者历史里近 7 天至少有 5 次成功记录时,本次超过中位数的 2 倍且多出 1 秒以上。
3. 看历史
python {baseDir}/scripts/check.py history --sites sites.json --days 7
python {baseDir}/scripts/check.py history --sites sites.json --days 30 --only 官网 --json -
输出每个站的检查次数、可用率、平均/P95 耗时、故障段数、最近状态、证书剩余天数,下面列出每一段故障的开始/恢复时间、连续失败次数和原因。可用率 = 成功次数 ÷ 检查次数,所以检查越密越准;故障时长的精度取决于检查间隔。
配合定时任务
脚本本身只跑一轮就退出,想持续巡检就交给定时任务。建议间隔 5~10 分钟;证书只需要每天看一次,但跟着一起查不费事。
- WorkBuddy 定时任务:把
sites.json放进「文档\网站巡检」,新建任务,每 5 分钟执行python {baseDir}/scripts/check.py run --sites <文档\网站巡检\sites.json 的绝对路径> --json <文档\网站巡检>/last.json --force(定时任务每次都要覆盖同一个last.json,所以要加--force);退出码非 0 时让助手读last.json并按下面「报告怎么说话」把问题讲给用户。每天早上加一条history --days 1做日报,每周一条history --days 7做周报。 - Linux / macOS crontab:
*/5 * * * * cd /home/me/uptime && python3 /path/to/scripts/check.py run --sites sites.json --out last.md --force || cat last.md - Windows 任务计划程序:操作填
python,参数填脚本路径\check.py run --sites D:\uptime\sites.json --out D:\uptime\last.md --force,「起始于」填D:\uptime(报告只能写在「起始于」目录或「文档\网站巡检」里)。 - 同一个清单不要同时开两个定时任务去跑,SQLite 会出现重复记录。
- 本机网络要走代理才能访问的站,把代理写进任务的环境变量
HTTPS_PROXY,或直接在命令里加--proxy。国内站点在走代理后耗时和 IP 都会失真,建议国内外分成两个清单、两条任务。 history.db会一直增长,每次一条记录,几十个站每 5 分钟跑一年也就几十 MB;要清理就删掉旧数据或整个库,删之前先跟用户确认。
报告怎么说话
- 先说结论一句话:「8 个站 6 个正常,1 个挂了,1 个证书 10 天后到期」。全部正常就只说这一句,外加最慢的是哪个。
- 按严重度讲问题,报告里「问题(按严重度)」段已经排好序:
- 宕机:说清是哪一层坏的——DNS 解析失败(域名到期或解析被删)、TCP 连不上(服务器或防火墙)、TLS 握手失败、超时、还是返回了错误状态码(5xx 是服务端出错,4xx 多半是地址变了或被拦了)。给一条下一步:比如「先在浏览器打开看看;5xx 去看服务器日志」。
- 证书:说剩几天、哪天到期、谁签发的,提醒「谁负责续、在哪续」。校验没通过时,照报告里的原因和下一步讲,并强调:访客在浏览器里会看到安全警告,基本等于打不开,所以这种情况按不可用计入可用率。
- 变慢:指出慢在哪一段——DNS 慢看解析服务商,连接慢看线路或机房,TLS 慢看证书链和服务器负载,首字节慢是后端程序或数据库慢。
- 关键词丢失:页面还能打开但内容不对,可能是错误页、维护页、被替换或被篡改,建议用户马上人工打开确认。
- 关键词无法判断:脚本没拿到完整、可读的页面(太大、解压失败、传输中断、不是文本),不能说明关键词还在,也不能说明丢了;照报告里的原因讲,请用户人工打开看一眼,或把关键词检查换到一个更小的页面。 - 一次只让用户处理最要紧的一两件,明细表是给用户对照用的,别在对话里逐行念。
- 偶发一次失败不要大惊小怪:只失败一次、下一轮就好了,说「一次偶发抖动,已恢复」;连续多次失败再按故障处理。看
history的故障时段来判断。 - 别夸大结论:检查是从用户这台机器发出去的,只代表「从这里访问」的情况,不代表全国或全球用户都访问不了;走了代理的结果更不能直接当作真实用户体验。
- 用具体数字,不用行话:说「首页 4.2 秒才开始返回内容,平时是 0.6 秒」,不说「TTFB 劣化」。
边界
- 只检查用户自己的站或有权监控的站。巡检别人的公开页面技术上可以,但不要为了盯竞争对手去高频跑,更不要借这个脚本去压别人的服务器。
- 频率有底线:定时任务间隔不低于 1 分钟,并发上限 16、同一主机最多 2 个请求;用户要求「每秒检查一次」「几百并发」时直接说明这是压测不是巡检,不做。
- 只读:脚本只发 GET、不带登录态、不提交任何数据;清单里不要写带账号密码或令牌的 URL,报告和历史库里会原样出现。
- 网络环境会影响结果:本机 DNS 可能被干扰或污染,解析出的 IP 不一定是真实地址;走代理时 DNS 由代理解析,报告里的 DNS 列只是本机参考值,连接阶段只计到代理为止,远端建连时间会算进 TLS 段。代理只支持
http://形式(CONNECT 隧道),不支持 SOCKS。 - 不执行 JS:单页应用、meta refresh / JS 跳转页的关键词检查可能误报;遇到这种情况把 URL 换成最终页面地址,或换一个服务端直出的关键词。
- 证书只看站点直接返回的那张:不检查整条证书链的每一张、不查吊销状态(OCSP/CRL),也不检查 HTTP/2、HTTP/3、IPv6 是否可用。
- 历史库是用户的数据:放在用户指定的目录,删除、清理之前先问用户。