应创建Laravel中间件,用$request->header('User-Agent')获取UA,以stripos()匹配config('app.ua_blocked')中的黑名单字符串,优先校验非空再匹配,返回403视图而非abort。

怎么写一个判断 User-Agent 的 Laravel 中间件
直接在 app/Http/Middleware 下新建类,用 $request->header('User-Agent') 取值,别依赖 $request->userAgent() —— 它会触发额外解析,且 Laravel 10+ 已标记为软弃用。
实操建议:
- 中间件构造函数里不传参,所有规则走配置或硬编码,避免 DI 复杂化
- 用
stripos()做子串匹配,比正则快,也够用(比如屏蔽 “curl”、“httpie”、“python-requests”) - 注意大小写:HTTP 头字段名不区分大小写,但
$_SERVER环境里可能转成大写加下划线,Laravel 的header()方法已做归一化,放心用 - 返回
response()->view('errors.403', [], 403)比直接abort(403)更可控,能统一走视图层
Laravel 中间件里 UA 黑名单怎么维护才不卡住请求
黑名单不能每次请求都 file_get_contents() 或查数据库。静态数组 + 配置驱动是底线。
实操建议:
- 把黑名单写进
config/app.php新增的'ua_blocked'键里,格式为['curl', 'wget', 'python-requests'] - 中间件中用
config('app.ua_blocked')读取,避免硬编码污染逻辑 - 如果真要动态更新(如后台开关),用 Laravel Cache 存数组,设置短 TTL(比如 60 秒),并配好缓存驱动(Redis > APCu > file)
- 千万别在中间件里调
DB::table()->get()—— 每次请求都查库,QPS 上不去还容易拖垮连接池
为什么有些爬虫 UA 过不了检查,但 dd($request->header('User-Agent')) 显示正常
因为真实请求可能被 CDN、反向代理(Nginx / Cloudflare)剥离或覆盖了 User-Agent,或者客户端发了空 UA。
常见错误现象:
- 本地
curl -H "User-Agent: curl"能拦截,但线上 Nginx 日志里看到 UA 是空的 - Cloudflare 默认会清洗部分 UA 字段,尤其带脚本特征的,得开 “Origin Cache Control” 并关掉 UA 相关 scrubbing
- Nginx 配置里写了
proxy_set_header User-Agent "";,等于主动清空 - 某些安卓 WebView 或小程序环境 UA 极简(如
"-"或空字符串),需单独判断empty($ua)
检查顺序必须是:!empty($ua) && in_array(...),否则空 UA 会跳过匹配,放行异常请求。
Laravel 11 的 TrustProxies 和 UA 检查冲突吗
不直接冲突,但会影响 $request->ip() 和请求头来源可信度 —— 如果你后续打算结合 IP + UA 做联合限制,就绕不开它。
关键点:
-
TrustProxies只影响X-Forwarded-For解析,不影响User-Agent本身 - 但如果你在中间件里用了
$request->server('HTTP_USER_AGENT')(不推荐),而代理没透传该头,就会拿不到值;务必坚持用$request->header('User-Agent') - 若启用
TrustProxies,确保前端代理(如 Nginx)显式透传:proxy_set_header User-Agent $http_user_agent; - Laravel 11 默认开启
APP_TRUSTED_PROXIES=*,开发时容易忽略这点,导致本地测试和线上行为不一致
UA 检查本身轻量,但一旦混入 IP 判断、频率限制或缓存策略,代理链路就变成隐性依赖项,漏掉一环就失效。










