本文约 9,062 字,预计阅读 23 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:验收靠”感觉”,出事靠运气
网站验收现场经常是这样:甲方打开首页看了一眼,”嗯,挺好”;点了两三个内页,”没问题”;然后在验收单上签字。三个月后发现:手机端某个页面排版错位、表单提交后收不到邮件、产品详情页的 SEO 描述全是空的、百度只收录了首页。
“感觉没问题”不是验收,”逐项打勾”才是。 一个中型企业站有几十个页面模板、上百个功能点,靠肉眼扫一遍最多覆盖 20%,剩下 80% 的问题会在上线后以”客户投诉”的形式陆续出现——那时候修复成本是验收阶段的十倍(因为要协调时间、要走变更流程、可能已经产生业务损失)。
一份合格的验收清单要解决三个问题:查什么(条目)、怎么查(方法)、什么算通过(标准)。下面这份清单按六大类组织,共 60 项,可以直接打印出来逐条打勾。
原理分析:验收的六个维度
维度一:功能正确性。 需求文档里的功能清单逐条测,重点是”边界情况”而不只是”正常流程”:表单必填项不填会怎样、上传超大文件会怎样、搜索无结果会怎样。
维度二:性能。 性能不是”快不快”的主观感受,而是可测量的指标。核心是 Google 提出的 Core Web Vitals 三个指标:
| 指标 | 含义 | 合格标准 | 优秀标准 |
|---|---|---|---|
| LCP(最大内容绘制) | 主要内容出现的时间 | ≤ 2.5 秒 | ≤ 2.0 秒 |
| INP(交互到下次绘制) | 交互响应性(2024 年取代 FID) | ≤ 200 ms | ≤ 100 ms |
| CLS(累积布局偏移) | 视觉稳定性,页面元素是否跳动 | ≤ 0.1 | ≤ 0.05 |
| TTFB(首字节时间) | 服务器响应速度 | ≤ 800 ms | ≤ 400 ms |
维度三:SEO。 分技术 SEO(能不能被抓取和索引)与内容 SEO(页面元素是否完整)。上线前必须查完,因为上线后修改 URL 和标题会有收录波动。
维度四:安全。 能不能被轻易攻破,出问题能不能恢复。
维度五:兼容性。 不同浏览器、不同设备、不同屏幕尺寸下的一致性。
维度六:交付物完整性。 源码、账号、文档、授权是否齐全。
保姆级实操:60 项验收清单
A. 功能验收(15 项)
| # | 检查项 | 通过标准 |
|---|---|---|
| A1 | 首页、栏目页、详情页、单页、搜索页、404 页 | 全部可访问,返回 200(404 页返回 404) |
| A2 | 导航菜单 | 各级菜单链接正确,当前项高亮正常 |
| A3 | 面包屑导航 | 层级正确,可点击返回上级 |
| A4 | 分页功能 | 上一页/下一页/页码正确,无重复内容 |
| A5 | 站内搜索 | 关键词返回结果正确,无结果时有友好提示 |
| A6 | 表单必填校验 | 必填项为空时阻止提交并提示 |
| A7 | 表单提交 | 提交成功后有提示,且后台能查到记录 |
| A8 | 表单邮件通知 | 提交后指定邮箱在 1 分钟内收到通知 |
| A9 | 表单防刷 | 验证码生效,短时间内重复提交被拦截 |
| A10 | 文件上传 | 格式与大小限制生效,超限有明确提示 |
| A11 | 图片上传与裁剪 | 上传正常,前台展示不变形 |
| A12 | 后台新增/编辑/删除内容 | 各类内容类型均可正常操作,前台同步更新 |
| A13 | 自定义字段 | 后台可填写,前台正确展示 |
| A14 | 第三方集成(地图/客服/支付) | 接口正常,无控制台报错 |
| A15 | 权限控制 | 编辑账号无法进入设置页,无多余权限 |
批量检查页面状态码:
#!/usr/bin/env bash
# check_pages.sh —— 批量检查关键页面状态码
SITE="https://www.mimangfei.com"
PAGES=(
"/" "/about/" "/contact/" "/products/" "/news/"
"/sitemap_index.xml" "/robots.txt" "/feed/"
"/wp-login.php" "/nonexistent-page-404-test/"
)
for p in "${PAGES[@]}"; do
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "${SITE}${p}")
time=$(curl -s -o /dev/null -w '%{time_total}' --max-time 10 "${SITE}${p}")
printf "%-35s %s %ss\n" "$p" "$code" "$time"
done
B. 性能验收(10 项)
| # | 检查项 | 通过标准 |
|---|---|---|
| B1 | 首页首屏加载(4G 模拟) | ≤ 2.5 秒 |
| B2 | LCP | ≤ 2.5 秒 |
| B3 | CLS | ≤ 0.1 |
| B4 | INP | ≤ 200 ms |
| B5 | TTFB | ≤ 800 ms |
| B6 | 页面总体积 | 首页 ≤ 2 MB |
| B7 | 图片格式与大小 | WebP 格式,单张 ≤ 200 KB |
| B8 | 缓存配置 | 页面缓存生效,静态资源有 Cache-Control 头 |
| B9 | 压缩 | Gzip 或 Brotli 已开启 |
| B10 | 数据库查询数 | 首页 ≤ 100 次查询(用 Query Monitor 插件查看) |
命令行快速测量:
# 1. 测量各阶段耗时(TTFB 与总耗时)
curl -s -o /dev/null -w \
'DNS解析: %{time_namelookup}s\nTCP连接: %{time_connect}s\nTLS握手: %{time_appconnect}s\n首字节TTFB: %{time_starttransfer}s\n总耗时: %{time_total}s\n下载大小: %{size_download} bytes\n' \
导航
# 2. 检查压缩是否生效
curl -sI -H 'Accept-Encoding: gzip, br' https://www.mimangfei.com/ \
| grep -iE 'content-encoding|content-length'
# 3. 检查静态资源缓存头
curl -sI https://www.mimangfei.com/wp-content/themes/主题/style.css \
| grep -iE 'cache-control|expires|etag'
# 4. 检查 HTTP/2 或 HTTP/3
curl -sI --http2 https://www.mimangfei.com/ | head -1
用 Lighthouse 命令行做完整评分(可集成到 CI):
# 安装 Lighthouse(需 Node.js 18+)
npm install -g lighthouse
# 对首页跑一次移动端评测,输出 HTML 报告
lighthouse https://www.mimangfei.com/ \
--only-categories=performance,seo,accessibility,best-practices \
--form-factor=mobile \
--screenEmulation.mobile \
--output=html --output-path=./report.html \
--chrome-flags="--headless --no-sandbox" \
--quiet
# 只输出分数(适合脚本化)
lighthouse https://www.mimangfei.com/ \
--only-categories=performance --output=json \
--chrome-flags="--headless --no-sandbox" --quiet \
| python3 -c "import sys,json; d=json.load(sys.stdin); \
print('性能分:', round(d['categories']['performance']['score']*100)); \
[print(f\" {k}: {v['displayValue']}\") for k,v in d['audits'].items() \
if k in ('largest-contentful-paint','cumulative-layout-shift','total-blocking-time','first-contentful-paint')]"
C. SEO 验收(15 项)
| # | 检查项 | 通过标准 |
|---|---|---|
| C1 | blog_public 设置 |
值为 1(允许索引) |
| C2 | robots.txt | 存在且未屏蔽有效目录 |
| C3 | 站点地图 | /sitemap_index.xml 可访问且包含所有内容类型 |
| C4 | 固定链接结构 | 简洁、含关键词、无日期(资讯站除外) |
| C5 | URL 大小写与字符 | 全小写,无中文与特殊字符 |
| C6 | 页面 title | 每页唯一,≤ 60 字符,含核心词 |
| C7 | Meta description | 每页唯一,120–155 字符 |
| C8 | H1 标签 | 每页有且仅有一个 |
| C9 | 标题层级 | H1→H2→H3 递进,不跳级 |
| C10 | 图片 alt 属性 | 所有内容图片均有描述性 alt |
| C11 | 结构化数据 | Organization/BreadcrumbList/Article 通过富媒体测试 |
| C12 | 内链 | 每个内容页有 3–5 个相关内链 |
| C13 | 死链检查 | 全站无 404 内链 |
| C14 | Canonical 标签 | 每页指向自身正确 URL |
| C15 | 统计与站长平台 | GA4/百度统计已部署,站长平台已验证 |
批量检查 SEO 元素:
# 1. 检查 robots.txt 是否误屏蔽
curl -s https://www.mimangfei.com/robots.txt
# 2. 检查 robots.txt 里声明的站点地图是否可访问
curl -s https://www.mimangfei.com/robots.txt | grep -i sitemap
# 3. 批量抓取页面并提取 title / description / h1
python3 -c "
import requests, re
from bs4 import BeautifulSoup
urls = ['https://www.mimangfei.com/', 'https://www.mimangfei.com/about/']
for u in urls:
r = requests.get(u, timeout=10)
s = BeautifulSoup(r.text, 'html.parser')
t = s.title.string if s.title else '(无)'
d = s.find('meta', attrs={'name':'description'})
h1 = s.find('h1')
canon = s.find('link', rel='canonical')
print(f'URL : {u}')
print(f' title : {t} (长度 {len(t)})')
print(f' desc : {(d.get(\"content\") if d else \"(无)\")}')
print(f' h1 : {h1.get_text(strip=True) if h1 else \"(无)\"}')
print(f' h1数量 : {len(s.find_all(\"h1\"))}')
print(f' canonical: {canon.get(\"href\") if canon else \"(无)\"}')
imgs_no_alt = [i.get('src') for i in s.find_all('img') if not i.get('alt')]
print(f' 缺alt图片: {len(imgs_no_alt)}')
"
# 4. 全站死链检查(用 wget 的 spider 模式)
wget --spider --recursive --level=2 --no-verbose \
--output-file=/tmp/crawl.log \
--wait=0.5 https://www.mimangfei.com/
grep -i "broken link\|404" /tmp/crawl.log | head -30
D. 安全验收(8 项)
| # | 检查项 | 通过标准 |
|---|---|---|
| D1 | HTTPS 全站启用 | HTTP 301 跳转 HTTPS,无混合内容 |
| D2 | SSL 证书 | 证书链完整,SSL Labs 评级 ≥ A |
| D3 | WP_DEBUG |
生产环境为 false |
| D4 | 后台文件编辑 | DISALLOW_FILE_EDIT 为 true |
| D5 | 后台登录保护 | 有失败次数限制,管理员启用双因素 |
| D6 | 敏感文件访问 | /wp-config.php、/readme.html 返回 403 |
| D7 | 备份机制 | 每日自动备份,异地存储,已验证可恢复 |
| D8 | 核心与插件版本 | 均为最新,无已知漏洞 |
# 1. 检查 HTTP 是否跳转到 HTTPS
curl -sI http://www.mimangfei.com/ | grep -i -E '^(HTTP/|location:)'
# 2. 检查证书有效期与签发者
echo | openssl s_client -connect www.mimangfei.com:443 -servername www.mimangfei.com 2>/dev/null \
| openssl x509 -noout -dates -issuer -subject
# 3. 检查敏感文件是否可访问(期望 403 或 404)
for f in /wp-config.php /readme.html /xmlrpc.php /.git/config; do
printf "%-20s %s\n" "$f" "$(curl -s -o /dev/null -w '%{http_code}' https://www.mimangfei.com$f)"
done
# 4. 检查是否泄露 WordPress 版本号
curl -s https://www.mimangfei.com/ | grep -i 'generator'
curl -s https://www.mimangfei.com/feed/ | grep -i 'generator'
# 5. 检查后台登录页是否有防护
for i in 1 2 3 4 5; do
curl -s -o /dev/null -w "第${i}次尝试: %{http_code}\n" \
-d 'log=admin&pwd=wrongpassword' https://www.mimangfei.com/wp-login.php
done
E. 兼容性与内容验收(7 项)
| # | 检查项 | 通过标准 |
|---|---|---|
| E1 | 移动端适配 | 375px 宽度下无横向滚动条 |
| E2 | 平板适配 | 768px 宽度下布局正常 |
| E3 | 浏览器兼容 | Chrome/Edge/Safari 最近两个大版本正常 |
| E4 | 长标题与长内容 | 不撑破布局,有截断或换行 |
| E5 | 内容完整性 | 文字无乱码,无 Lorem ipsum 占位符 |
| E6 | 图片完整性 | 无裂图,无拉伸变形 |
| E7 | 交互反馈 | 按钮有 hover/active 状态,加载有提示 |
# 检查是否残留占位内容(交付前必查)
wp post list --post_type=any --format=csv --fields=ID,post_title \
| grep -iE "lorem|ipsum|测试|test|示例|demo|hello world|又一个"
# 检查是否有默认内容未清理
wp post list --post_type=post --format=count # "世界,你好" 应已删除
wp post list --post_type=page --format=csv --fields=ID,post_title
wp option get blogdescription # 不应是"又一个 WordPress 站点"
F. 交付物验收(5 项)
| # | 检查项 | 通过标准 |
|---|---|---|
| F1 | 源码交付 | Git 仓库可访问,含子主题与自定义插件 |
| F2 | 账号交接 | 域名、服务器、后台、第三方服务账号均归甲方 |
| F3 | 授权凭证 | 商业主题/插件以甲方名义购买,凭证已移交 |
| F4 | 文档交付 | 使用手册、环境说明、应急预案齐全 |
| F5 | 培训完成 | 已录屏或现场培训,客户能独立完成高频操作 |
验收问题记录模板(发现问题时逐条登记,避免口头沟通遗漏):
## 验收问题清单
| 编号 | 模块 | 问题描述 | 严重程度 | 发现日期 | 状态 | 修复日期 |
|---|---|---|---|---|---|---|
| B-01 | 产品详情页 | iPhone Safari 下参数表格溢出屏幕 | 高 | 2026-10-10 | 待修复 | — |
| C-02 | 全站 | 产品图片 alt 属性为空 | 中 | 2026-10-10 | 已修复 | 2026-10-11 |
| A-03 | 联系表单 | 提交后 5 分钟才收到邮件 | 中 | 2026-10-10 | 待修复 | — |
严重程度定义:
- 高:影响核心功能或造成业务损失,必须修复后才能验收
- 中:影响体验但不阻断流程,可约定修复期限
- 低:优化建议,可延后处理
常见踩坑
- 只在自己的浏览器和手机上测。 开发者的设备上通常有缓存、有登录态、屏幕尺寸单一。至少要用无痕模式 + 真实手机 + 至少两种浏览器测试。
- 只测”正常流程”不测边界。 表单不填必填项、上传 10 MB 图片、搜索特殊字符——这些才是高频出问题之处。
- 忘了检查
blog_public。 开发期勾了”阻止索引”,上线没收。用wp option get blog_public一行命令就能避免。 - 验收时用的是缓存后的页面。 CDN 或页面缓存返回的是旧内容,看起来正常,但源站是坏的。验收前先清缓存,或直接绕过缓存(加随机查询参数)。
- 性能测试只跑一次。 首次访问无缓存,后续有缓存,两者差异巨大。性能测试要跑”冷启动”和”预热后”两种状态,且至少测三次取中位数。
- 验收完没签字确认。 口头”没问题”三个月后会变成”当时就没做好”。验收单必须双方签字,并保留问题清单。
- 案例:一次”验收通过但全站无索引”的事故。 客户验收时逐页点了,功能都正常,签字付款。两个月后客户问”为什么百度搜不到我们”,排查发现
blog_public值为 0——开发期为了防止演示站被收录而设置,上线时忘了改。更糟的是这两个月里站点地图已提交、外链已建设,但由于robots.txt全站禁止,搜索引擎从未真正抓取过正文,两个月的 SEO 工作完全无效。教训:验收清单里必须有 C1(blog_public)和 C2(robots.txt)这两项,且要用命令确认值,而不是”看后台那个勾选框”(后台显示可能因缓存而不同步)。这类”看不见的开关”是验收中最容易漏掉、后果最严重的部分。
总结
验收的判断标准是”每一项都有明确的检查方法和通过标准,且结果被记录”。执行顺序:先跑自动化批量检查(状态码、SEO 元素、安全头、占位内容)→ 再做人工逐项核对(功能清单、兼容性、交付物)→ 发现问题登记到问题清单并定严重程度 → 修复后复测 → 双方签字。60 项清单里,A(功能)、C(SEO)、D(安全)三类的”开关型”配置最容易漏(blog_public、WP_DEBUG、表单收件邮箱、DISALLOW_FILE_EDIT),建议用命令批量确认而不是靠肉眼。验收通过后的交付环节参照 客户项目交付注意事项,清单本身应在 网站需求文档怎么写 阶段就作为合同附件确定下来,避免验收时双方对标准产生分歧。
