Laravel IP白名单中间件核心是用$request->ip()获取真实IP并与配置白名单比对,不匹配则返回403;关键需正确配置TrustProxies、支持CIDR/IPv6、避免CLI误拦、禁用abort(403)。

直接说结论:Laravel 自定义中间件做 IP 白名单,核心是读取请求 $request->ip(),比对预设白名单数组或配置项,匹配失败就返回 403;但必须注意 CLI 环境、代理转发、IPv6 和缓存策略这几个坑。
怎么写一个基础的 IP 白名单中间件
用 php artisan make:middleware IpWhitelist 创建后,在 handle() 方法里判断即可。关键不是“怎么写”,而是“怎么判断才可靠”:
- 白名单来源建议从配置文件读取(如
config/whitelist.php),避免硬编码;可支持 CIDR 格式(如192.168.1.0/24)需额外解析,基础版先用精确匹配 - 务必调用
$request->ip()而非$_SERVER['REMOTE_ADDR'],Laravel 已处理部分代理逻辑 - 不匹配时用
return response('', 403)或抛出HttpException(403),别用abort(403)—— 它会触发异常渲染流程,增加开销
为什么 $request->ip() 在某些部署下不准
常见于 Nginx + PHP-FPM 或云负载均衡后:真实 IP 被藏在 X-Forwarded-For 或 X-Real-IP 头里,而 Laravel 默认只信任 localhost 为可信代理。解决方法:
- 在
App\Http\Middleware\TrustProxies中显式设置$proxies,例如['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],或直接写死负载均衡器 IP - 确认
$headers属性包含Illuminate\Http\Request::HEADER_X_FORWARDED_FOR(默认已设) - 如果用了 Cloudflare,还需在 TrustProxies 中加入 Cloudflare 的 IP 段,并启用
APP_TRUSTED_PROXIES=*(仅限内网环境,生产慎用)
如何支持 CIDR 和 IPv6 白名单
原生 PHP 不带 CIDR 匹配函数,别自己手写位运算。推荐轻量方案:
- 用
symfony/ip-converter(Laravel 9+ 已自带Symfony\Component\HttpFoundation\IpUtils) - 调用
IpUtils::checkIp($request->ip(), $whitelist),它自动识别 CIDR、IPv4/IPv6 混合列表,且性能足够 - 白名单配置示例:
['127.0.0.1', '192.168.0.0/16', '::1', '2001:db8::/32'] - 注意:若白名单含域名(如
api.example.com),IpUtils::checkIp()会静默跳过,不会报错 —— 要提前过滤或报 warning
中间件注册和生效范围要小心
白名单中间件不是越早执行越好。错误做法是加到 $middleware 全局数组里 —— 这会让 Artisan 命令、队列监听、甚至 Horizon Web 界面都受限制。
- 只注册到
$middlewareGroups['web']或$routeMiddleware,然后按需绑定到具体路由组,例如:Route::middleware('ip.whitelist')->group(...) - 测试时用
php artisan tinker手动调用app('request')->ip()确认当前环境返回值,比猜强得多 - 上线前务必验证 CLI 场景:运行
php artisan route:list不应触发白名单检查 —— 如果触发了,说明中间件挂载位置错了
最常被忽略的是:开发环境用 Valet 或 Sail 时,$request->ip() 可能返回 127.0.0.1,但生产 Nginx 配置里忘了传 X-Real-IP,结果线上所有请求都被拦掉。上线前拿 curl 加头手动测一次,比看十遍文档管用。


















