Beego 防爬应聚焦行为识别与请求合法性校验:在 BeforeRouter 阶段校验 Header、JSON 结构、Referer;用 BeforeStatic 拦截私有静态资源;以 per-IP 限流为底线,且限流须置于所有校验之前。

Beego 没有“阻止爬虫”的内置中间件,也不能靠 User-Agent 黑名单一劳永逸。真正有效的做法是:在关键路径上叠加行为识别 + 请求合法性校验,而不是试图识别“是不是爬虫”。
为什么 User-Agent 过滤基本无效
几乎所有成熟爬虫(包括 Requests、Scrapy、Playwright)都默认携带浏览器 UA,且可任意伪造;Beego 的 ctx.Request.UserAgent() 字符串匹配极易被绕过。更严重的是,用它做第一道拦截会漏掉真实用户(比如某些隐私浏览器禁用 UA 上报),还可能误杀搜索引擎爬虫(影响 SEO)。
实际应把 UA 仅作为辅助线索,不用于直接拒绝请求。
在 BeforeRouter 阶段做请求头与参数联合校验
这是拦截恶意批量请求最可控的位置——所有非静态请求都会经过,且上下文完整(ctx.Input.Param() 可用、ctx.Input.RequestBody 已缓存(需提前设 beego.BConfig.CopyRequestBody = true))。
- 检查必要 Header:比如要求
X-Requested-With: XMLHttpRequest(AJAX 请求常用),或自定义 Token 头(如X-App-Key),缺失则ctx.Abort(400, "bad request") - 验证请求体结构:对 POST/PUT 接口,强制 JSON body 含特定字段(如
"timestamp"和"signature"),并校验签名有效期(±30 秒) - 拒绝空 Referer 或非法 Referer:若接口只供自家前端调用,可检查
ctx.Request.Referer()是否以你域名开头;但注意移动端 WebView 或 PWA 场景下 Referer 可能为空
用 BeforeStatic 拦截私有静态资源爬取
爬虫常通过枚举 /static/ 路径下载敏感文件(如 /static/configs/db.yaml、/static/uploads/user123/xxx.pdf)。这类请求不走路由,BeforeRouter 完全无效。
必须用 beego.BeforeStatic 阶段注册过滤器,并手动解析路径:
func BlockStaticCrawler(ctx *context.Context) {
path := ctx.Request.URL.Path
// 只拦截私有目录,放行公开资源(如 /static/css/、/static/js/)
if strings.HasPrefix(path, "/static/private/") || strings.Contains(path, "..") {
// 检查 session 或 token(注意:BeforeStatic 中 ctx.Input.Session 可用,但 ctx.Controller 未初始化)
if ctx.Input.Session.Get("user_id") == nil {
ctx.Abort(403, "Forbidden")
return
}
}
ctx.Next()
}
// 注册时必须带前缀匹配
beego.InsertFilter("/static/", beego.BeforeStatic, BlockStaticCrawler)
⚠️ 注意:ctx.Input.Param(":id") 在 BeforeStatic 里不可用,提取路径参数得靠 strings.Split(path, "/") 或正则。
IP 级限流是防爆破的底线措施
单纯“阻止爬虫”不如“让爬虫无法高频获取数据”。用 golang.org/x/time/rate 在 BeforeRouter 做 per-IP 限流:
- 对登录、搜索、详情页等高价值接口,设低阈值(如 5 次/分钟)
- 对列表页等中等价值接口,设中等阈值(如 30 次/分钟)
- 务必用
ctx.Input.IP()(不是RemoteAddr),并处理代理场景(如检查X-Forwarded-For) - 限流后返回
429 Too Many Requests,并带上X-RateLimit-Remaining头,避免客户端盲目重试
最易被忽略的一点:限流逻辑必须放在所有其他校验之前,否则攻击者可通过构造非法请求(如超长 URL、畸形 body)绕过鉴权直接打垮服务。











