📚 离职知识库

网站反爬加固 —— 5 套可落地方案

这个文件夹整理了 5 套「给个人站/小站做反爬与反自动化加固」的方案,用于以后自建站时直接取用。 每套方案对应防御纵深里的一层或几层,都是从 Cloudflare/OWASP 官方文档、公开反爬技术资料与真实站点防护行为整理而来,各附一份参考文档(讲透原理)和一份 Claude Code Skill(照着做)。

#一个核心认知(先读这句再看方案)

网页反爬的本质是经济成本博弈,不是造一座绝对攻不破的堡垒。 你每一层手段的真实作用是「抬高爬虫方的工程成本和金钱成本」,把抓你的站变得不划算,从而劝退绝大多数机会主义脚本。对有预算有决心的对手,几乎所有单一手段都有成熟的商业绕过方案。

所以正确策略是多层叠加、成本导向:用零成本手段挡掉 90% 的低质量脚本,用托管服务挡掉多数中等自动化工具,把有限预算留给真正有价值的核心数据/接口。

#5 套方案速览

01 托管墙 02 自建墙 03 前端风控 04 内容护盾 05 接口鉴权
防御层 网络层+验证层 应用层+行为层 前端 JS 层 内容层 HTTP+应用层
抓手 Cloudflare 一键接入 限流+蜜罐+时序日志 指纹+无头检测 字体加密+结构混淆 签名+动态 Token
开发成本 几乎为零 低(自建,0.5-2 人日)
依赖第三方 是(Cloudflare) 半(可用开源库)
伤 SEO/体验 几乎不 轻微 严重(双刃剑)
主要挡谁 通用爬虫/DDoS 粗暴脚本/垃圾注册 刷单/薅羊毛/API 防刷 核心数据被复制 接口重放/批量调用

#怎么选

  • 只想花最少力气拿到最大防护 → 直接上 01 托管墙,DNS 切到 Cloudflare 免费版,几分钟见效,个人站首选。
  • 不想依赖第三方、想自己掌控 → 走 02 自建墙,Nginx + 应用层规则,零成本、零外部依赖。
  • 有登录/下单/投票,怕被刷 → 加 03 前端风控,识别无头浏览器与异常设备。
  • 有自己独家整理的数据不想被竞品扒走 → 谨慎评估 04 内容护盾,注意它会伤 SEO 和无障碍。
  • 有开放 API 或移动端 → 用 05 接口鉴权,把接口和公开网页流量分开保护。

推荐组合(个人站现实防线)01 托管墙 + 02 自建墙 + Turnstile 无感验证。这一套零到极低成本,能挡掉绝大多数爬虫,是投入产出比最高的组合。04、05 属于按需加装的专项。

#零成本合规底座(每个站都该做,但它不拦爬虫)

robots.txt + 服务条款反爬条款不是技术屏障,恶意爬虫可完全无视。它们的价值是事后追责取证:法院会把「明知禁止仍强抓」作为认定恶意的证据。

  • robots.txt:声明禁止抓取的路径,零成本必做。
  • ToS 反爬条款:Clickwrap(主动勾选同意)效力远强于 Browsewrap(页脚放链接);登录场景下有实际法律效力,未登录访问公开页则约束力弱(参考 Meta v. Bright Data 2024)。
  • 抓取公开数据在美国 Ninth Circuit 辖区(hiQ v. LinkedIn)CFAA 威慑力有限,但一旦绕过登录/付费墙/IP 封禁再继续访问,责任性质就变了。

#每个子文件夹里有什么

  • 参考文档.md:讲清这套方案的完整原理、真实案例、破解难度与从零搭建的技术选型。
  • SKILL.md:一份写给 Claude Code 的技能说明书,以后建站时可直接触发它照着施工。

#方案:01-托管墙-Cloudflare一键防护


#name: harden-cloudflare-managed-wall description: 给一个网站接入 Cloudflare 做零开发的托管级反爬防护(Bot Fight Mode + WAF + 限流 + Turnstile 无感验证)。当用户说「给站点加反爬」「防止被爬」「上 Cloudflare 防护」「加个验证码防机器人」时使用。

#托管墙 —— Cloudflare 一键防护

这份技能教你给一个站做托管墙:把 DNS 切到 Cloudflare,让边缘节点在流量回源前完成风控。核心铁律:能让 Cloudflare 在门口挡掉的流量,绝不放进你的服务器

参考机制详见同目录 参考文档.md(基于 Cloudflare 官方文档与真实站点拦截行为整理)。

#什么时候用这套

  • 想用最小力气拿最大防护、能接受依赖 Cloudflare → 用这套,个人站首选。
  • 明确不想依赖任何第三方、要完全自己掌控 → 别用这套,用 02-自建墙 那套。
  • 两者建议叠加:托管墙挡外层,自建墙兜底应用层业务规则。

#架构总览

[用户/爬虫] → [Cloudflare 边缘:风险评分/挑战/限流] → [你的源站]
                        ↑ 表单请求先过 Turnstile 校验

#实现步骤(按顺序执行)

#1. 接入 DNS

  • 在 Cloudflare 添加站点,把域名 NS 改到 Cloudflare 分配的地址,等生效。
  • 源站 IP 记录设为 Proxied(橙色云),否则防护不生效、还暴露源站真实 IP。

#2. 开边缘防护

  • Bot Fight Mode(Security → Bots)。
  • Verified Bots 放行:确认搜索引擎白名单开启,避免误伤 SEO。

#3. 配限流规则

  • Security → WAF → Rate limiting rules,给 /login /api/* /comment 等敏感路径设阈值。
  • 维度尽量用 IP + Cookie/账号 组合,别只用 IP。

#4. 嵌 Turnstile 无感验证

  • 登录/注册/评论表单前端引入 Turnstile widget,拿到 token。
  • 后端必须siteverify 接口校验 token 通过才处理请求。

#验收清单(务必逐条自测)

  • curl -I https://你的域名 响应头出现 server: cloudflarecf-ray
  • 用数据中心 IP 的脚本狂刷敏感路径,会被限流/质询。
  • Googlebot(可用 UA + 反查验证)能正常访问,未被误伤。
  • 前端伪造 Turnstile token 提交,被后端 siteverify 拒绝。
  • 真实用户正常浏览全程无感,没被反复弹挑战。

#红线(做错就白做)

  1. DNS 记录没开 Proxied —— 灰色云=防护完全不生效,且泄露源站 IP,攻击者可绕过 Cloudflare 直连。
  2. Turnstile 只做前端不做后端校验 —— token 可伪造,等于没验。
  3. 限流/WAF 一刀切封 UA/国家 —— 误伤搜索引擎和真实用户,掉收录、丢流量。
  4. 指望免费版挡住住宅代理+真无头浏览器 —— 那是付费 Bot Management 的战场,别自欺。

#技术选型建议

  • 边缘防护:Cloudflare 免费版(Bot Fight Mode + WAF Custom Rules + 基础限流)。
  • 无感验证:Cloudflare Turnstile(免费无限量、隐私最好、不采集鼠标轨迹)。
  • 预算允许再升 Pro(约 $20/月量级)拿 Super Bot Fight Mode 的分类别动作。

#附:只单独引入 Turnstile 加固 saveme505 密码门(不迁 DNS)

场景:站跑在 Vercel、DNS 在 vercel-dns,不想把整站迁到 Cloudflare 边缘,但想给 *.saveme505.help 的统一密码门(见 ~/Desktop/配置信息/站点访问密码门.md)挡暴力破解。 Turnstile 可以脱离 DNS 托管独立使用,只在后台建 widget 即可,是这里性价比最高的一刀。

#为什么加在这里

11 个带门站对陌生人唯一开放的入口是 /api/login,现在防爆破只有一句 setTimeout 600ms。 脚本能并发狂刷这个接口猜 HUB_SITE_PASSWORD。Turnstile 让机器人拿不到有效 token, 后端在校验密码前就拒掉,堵的正是这个洞。

#前置(手动,只做一次)

Cloudflare 后台 → Turnstile → 建 widget,Hostname 填 saveme505.help(覆盖所有子域), 拿到一对 Site Key(公开) + Secret Key(保密),全站通用一对即可。

#改动(2 个门文件 + 每站 2 个 env)

app/login/page.tsx(前端)

  • 顶部引入脚本:<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer />
  • 表单里放:<div className="cf-turnstile" data-sitekey={SITEKEY} data-callback="onTs" />
  • token 存进 state,提交时 body: JSON.stringify({ password, turnstileToken })
  • 拿到 token 前禁用「进入」按钮

app/api/login/route.ts(后端,在 checkPassword 之前插入)

const verify = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({ secret: process.env.TURNSTILE_SECRET_KEY!, response: turnstileToken }),
}).then((r) => r.json());
if (!verify.success) return NextResponse.json({ error: "人机验证失败" }, { status: 403 });
// 原有的 600ms 延迟保留,作为纵深兜底

③ 每个站的 Vercel 环境变量

  • NEXT_PUBLIC_TURNSTILE_SITEKEY(公开)
  • TURNSTILE_SECRET_KEY(保密,别提交进仓库)

#落地顺序

先拿 hub 做试点跑通(改 2 文件 + 配 2 env + 重新部署,验证真人无感、脚本被 403), 再把改动复制到其余 10 个站,并同步进「给新站加门」的 4 文件模板里。

#边界(诚实提醒)

Turnstile 只护「登录这一步」,不护站内部的业务 API。要护那些得靠 02-自建墙 的应用层限流。 但对「密码门别被爆破」这个诉求,这一刀就够了。


#方案:02-自建墙-限流蜜罐与请求时序


#name: harden-selfhosted-anti-scrape-wall description: 在自己的 Nginx/应用层做零成本、无第三方依赖的反爬(限流 + 蜜罐字段/链接 + 请求时序日志封禁)。当用户说「不想依赖 Cloudflare 做反爬」「自己写规则防爬虫」「防垃圾注册/垃圾评论」「加个限流」时使用。

#自建墙 —— 限流、蜜罐与请求时序

这份技能教你不依赖任何第三方,在自己的服务器上做自建墙:限流卡频率、蜜罐设陷阱、日志看时序。核心铁律:每一条规则都要能区分「真人浏览器」和「脚本」,而不是无差别拦截

参考机制详见同目录 参考文档.md(基于 OWASP/Nginx 官方文档整理)。

#什么时候用这套

  • 明确不想依赖第三方、要完全自己掌控 → 用这套。
  • 想花最少力气拿最大防护、能接受依赖 Cloudflare → 用 01-托管墙 那套更省事。
  • 最佳实践是叠加:托管墙挡外层通用爬虫,自建墙兜底业务规则(防垃圾注册/评论)。

#架构总览

[请求] → [Nginx limit_req 限流] → [应用层蜜罐校验] → [业务]
              ↑                         ↑
        Fail2ban 扫日志封 IP      时间戳陷阱校验填写耗时

#实现步骤(按顺序执行)

#1. Nginx 限流

limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
location /api/ { limit_req zone=perip burst=20 nodelay; }
  • 敏感路径(登录/评论/API)单独设更严阈值。

#2. 埋蜜罐字段

  • 表单加一个 CSS 隐藏的 type="text" 诱饵字段,命名 website
  • 后端:该字段非空 → 静默丢弃请求(别返回错误,免得爬虫调试)。

#3. 埋时间戳陷阱

  • 表单渲染时嵌入 form_render_time 隐藏值。
  • 提交时后端校验 now - render_time > 2s,秒填即拒。

#4. 埋蜜罐链接 + Fail2ban

  • 页面放 display:none/trap-xxx 链接。
  • Fail2ban 规则:访问日志出现 /trap-xxx 或「只打 HTML 不拉子资源+高频」→ 封 IP 一段时间(设过期)。

#验收清单(务必逐条自测)

  • 脚本高频打 /api/ 会收到 429。
  • 自动填充所有字段(含蜜罐 website)的提交被静默丢弃。
  • 表单渲染后 1 秒内提交被拒绝。
  • 访问 /trap-xxx 后该 IP 被 Fail2ban 临时封禁。
  • 真实用户正常填表、正常浏览不被误伤,封禁能自动过期。

#红线(做错就白做)

  1. 限流只用 IP 维度 —— NAT 共享 IP 会误伤一片真实用户,务必叠加 Cookie/账号。
  2. 蜜罐用 type=hidden —— 爬虫专门跳过 hidden,必须用 CSS 隐藏正常字段。
  3. Fail2ban 永久封 IP —— IP 会被真实用户复用,必须设封禁时长。
  4. 以为这套能挡住无头浏览器 —— 完整无头浏览器能绕过,但它的资源成本被你抬高了,这就是目的。

#技术选型建议

  • 限流:Nginx limit_req(零成本首选);有业务维度需求上 Redis + Lua 滑动窗口。
  • 蜜罐:前端 CSS 隐藏 + 后端判断,或 django-honeypot 等现成库。
  • 日志封禁:Fail2ban;想要图形化规则可自托管开源 SafeLine WAF。

#方案:03-前端风控-设备指纹与无头检测


#name: harden-frontend-fingerprint-risk description: 在前端做设备指纹 + 无头浏览器检测的风控层,护登录/下单/投票等场景防刷单薅羊毛。当用户说「防刷单」「防薅羊毛」「防批量注册」「识别无头浏览器」「加设备指纹」时使用。

#前端风控 —— 设备指纹与无头检测

这份技能教你给关键操作加前端风控层:采指纹认设备、检测揪无头浏览器。核心铁律:指纹和检测只产出风险信号,最终封禁必须结合后端综合评分,绝不靠单一前端信号下手

参考机制详见同目录 参考文档.md(基于 FingerprintJS/botd/creepjs 等开源项目整理)。

#什么时候用这套

  • 有登录/下单/投票/抽奖,怕被批量刷 → 用这套。
  • 只是防内容被读取抓取 → 别用这套(性价比低),用 01-托管墙 + 02-自建墙
  • 建议叠在托管墙之上:Cloudflare 挡外层,前端风控护高价值操作。

#架构总览

[页面加载] → [指纹 JS + 无头检测 JS] → [加密上报] → [后端综合评分]
                                                        ↓ 高风险
                                              [拒绝 / 降级 / 二次验证]

#实现步骤(按顺序执行)

#1. 接入设备指纹

  • 前端引入 FingerprintJS 开源版,页面加载生成设备 ID。
  • 后端存储设备 ID,用于识别「同一设备换 Cookie/换账号」。

#2. 加无头检测

const flags = {
  webdriver: navigator.webdriver === true,
  noChrome: !window.chrome,
  swRenderer: /SwiftShader|llvmpipe/.test(getWebGLRenderer()),
};
  • 命中任一强信号 → 标记高风险上报。

#3. 一致性校验

  • 后端比对「浏览器时区 vs IP 归属地时区」,不符加风险分。

#4. 分级处置

  • 低风险放行;高风险要求 Turnstile 或短信二次验证,不直接封。

#验收清单(务必逐条自测)

  • 默认 Selenium/Playwright 访问,navigator.webdriver 被检出。
  • 无头 Chrome 的 WebGL 渲染器命中 SwiftShader 被标记。
  • 同一设备清 Cookie 后仍被识别为同一设备指纹。
  • 真实用户(含用 Brave/隐私模式的)不被直接封,最多走一次二次验证。
  • 上报数据是加密/混淆的,不是明文指纹字段。

#红线(做错就白做)

  1. 靠单一指纹直接封号 —— 隐私浏览器会让真实用户指纹漂移,误伤严重。
  2. 明文上报指纹信号 —— 等于把「我在采什么」告诉爬虫,一逆向就被针对性伪造。
  3. 只做前端不做后端评分 —— 前端 JS 可被无头环境完整执行并篡改上报值,最终判定必须在后端。
  4. 以为能挡住 stealth 工具 —— puppeteer-stealth 类专门 patch 这些痕迹,你挡的是没伪装的大多数,不是全部。

#技术选型建议

  • 指纹:FingerprintJS 开源版(MIT,免费);商业 Pro 版性价比对个人站一般,除非有明确反薅羊毛业务。
  • 无头检测:botd/creepjs 的检测清单自建,先做 navigator.webdriver + WebGL 强信号。
  • 二次验证:Cloudflare Turnstile(无感、免费)。

#方案:04-内容护盾-字体加密与结构混淆


#name: harden-content-obfuscation-shield description: 用字体 glyph 映射、CSS 排序混淆、结构随机化保护网页核心数据(价格/评分/独家数据)不被直接抓 DOM 复制。当用户说「防止核心数据被扒」「价格/数据被抄」「内容防复制」「字体加密」时使用。注意:这套会伤 SEO 和无障碍,务必先评估。

#内容护盾 —— 字体加密与结构混淆

这份技能教你给核心数据做内容层混淆:让直接抓 DOM 拿到乱码。核心铁律:只护真正值钱的核心数据,绝不全站铺开——这是会伤 SEO 和无障碍的双刃剑

参考机制详见同目录 参考文档.md(基于点评/票务类站点公开渲染行为整理)。

#什么时候用这套

  • 有自己独家整理的数据(报价体系/数据库)不想被竞品直接复制 → 谨慎评估后用。
  • 只是普通内容站、希望被搜索引擎良好收录 → 别用这套,它会掉收录,用 01+02 即可。
  • 用之前先自问:护这份数据的收益,真的大于掉 SEO + 伤无障碍 + 伤复制体验吗?

#架构总览

[服务端渲染] → 核心数字用自定义字体编码替换(1→a) → 下发乱码HTML+字体文件
                                                      ↓
                                          [浏览器加载字体渲染→人看到正确值]
                                          [搜索引擎→走白名单拿正常内容]

#实现步骤(按顺序执行)

#1. 先上零成本的结构随机化

  • 用 CSS Modules / CSS-in-JS,class 名自动随机化,破坏固定选择器爬取。这一步几乎无副作用,先做。

#2. 核心数据上字体映射

  • fonttools 生成子集化字体,把字符做 glyph 重映射(如 1→a)。
  • 服务端渲染时把真实数字替换成映射字符,配套下发该字体文件。

#3. 给搜索引擎开例外

  • 对 Verified Bots(真 Googlebot/Bingbot)下发正常未混淆内容,否则收录乱码。

#4. 定期轮换映射表

  • 排期轮换 glyph 映射,避免爬虫逆向一次长期复用。

#验收清单(务必逐条自测)

  • curl 直接抓页面,核心数字位置是乱码/无意义字符。
  • 真实浏览器打开,数字正常显示。
  • Googlebot(验证过的真爬虫)拿到的是正常内容,收录不受影响。
  • 评估过并接受:屏幕阅读器、用户复制粘贴对混淆内容的影响。
  • 映射表有轮换计划,不是一次性写死。

#红线(做错就白做)

  1. 全站正文都上字体混淆 —— 杀敌一千自损八百,SEO 和无障碍全崩。
  2. 不给搜索引擎开例外 —— Googlebot 抓到乱码,收录直接归零。
  3. 映射表永不轮换 —— 爬虫逆向一次长期复用,等于没防。
  4. 以为能挡住所有爬虫 —— 内容终究要显示给人,能截图就能 OCR,你挡的是省事爬虫。

#技术选型建议

  • 结构随机化:CSS Modules / CSS-in-JS(零成本,先做这个)。
  • 字体处理:fonttools(Python 开源库)做子集化 + glyph 重映射。
  • 搜索引擎例外:结合 CDN 的 Verified Bots 白名单。
  • 强烈建议:能用前四套方案解决就别碰这套,它的维护成本和副作用最高。

#方案:05-接口鉴权-签名与动态Token


#name: harden-api-auth-signing description: 给开放 API / 移动端接口加鉴权防重放(Referer/Origin 校验 + CSRF Token + 动态 Nonce + HMAC 签名/OAuth2)。当用户说「接口防重放」「API 防刷」「保护开放接口」「加签名鉴权」「防止接口被直接调用」时使用。

#接口鉴权 —— 签名与动态 Token

这份技能教你给接口加鉴权:用一次性、限时、可验证的凭证把重放请求逼成多步交互。核心铁律:密钥绝不硬编码到前端 JS 或移动端 APK——那是全套防线的命门

参考机制详见同目录 参考文档.md(基于 AWS SigV4 / OAuth 官方文档整理)。

#什么时候用这套

  • 有开放 API 或移动端 App,接口被直接调用/重放 → 用这套。
  • 纯 Web 展示站、只是防内容被读 → 别用这套(过度设计),用 01+02
  • 可与 03-前端风控 配合:鉴权管「凭证合不合法」,风控管「环境可不可疑」。

#架构总览

[客户端] --签名(HMAC+timestamp)--> [服务端复算校验+时间窗口] --> [业务]
              ↑ 密钥只在服务端,客户端转发已签名请求

#实现步骤(按顺序执行)

#1. 打底:来源校验

  • Nginx valid_referers / 框架 CSRF 配置,挡掉不带 Referer/Origin 的懒脚本。

#2. 写操作加 CSRF Token

  • 用框架内置 CSRF 中间件(Django/Rails/Spring Security),护住 POST/PUT/DELETE。

#3. 关键操作加动态 Nonce

  • 渲染时签发一次性 nonce 存 Redis(带 TTL + 限用一次),提交时校验并作废。

#4. API 加 HMAC 签名

signature = HMAC(secret, method + path + body + timestamp)
  • 服务端复算比对 + 校验 timestamp 在 ±5 分钟窗口内。
  • secret 只存服务端;移动端若必须持有,走安全存储 + 混淆,并接受被逆向的残余风险。

#验收清单(务必逐条自测)

  • 不带 Referer/Origin 的请求被拒。
  • 抓一个合法请求原样重放,因 timestamp 过期/nonce 已用而失败。
  • 篡改请求体后签名校验失败。
  • CSRF Token 缺失/错误时写操作被拒。
  • 全局搜代码,确认密钥没有硬编码进任何前端/客户端产物。

#红线(做错就白做)

  1. 密钥硬编码进前端/APK —— 逆向即提取,签名逻辑被复现后零成本重放,全套失效。
  2. 签名不含时间戳/无有效窗口 —— 一个合法签名可无限重放。
  3. OAuth 注册零门槛 —— 批量注册马甲账号绕过一切限流。
  4. 为纯 Web 站单独引入 HMAC —— 过度设计,密钥必然下发、易逆向,性价比低。

#技术选型建议

  • 来源/CSRF:框架内置能力(Django/Rails/Spring Security),零额外成本。
  • 动态 Nonce:Redis + TTL 自建(简单版 1-2 人日)。
  • 签名:AWS SigV4 或自实现 HMAC;网关侧 Key 管理用 Kong / APISIX。
  • 身份:OAuth2,托管用 Auth0/Okta,自建注意加注册门槛。

来源:沉淀/反爬机制/README.md 及其下 5 套 SKILL.md(01-托管墙 / 02-自建墙 / 03-前端风控 / 04-内容护盾 / 05-接口鉴权)(整理于 2026-08-18)