应优先在 Nginx 层用 geo+map 实现白名单拦截,其次配置 Laravel 可信代理并使用 $request->ip() 结合 config('ip-whitelist.allowed') 校验,注意代理层级、环境差异及 CLI 兼容性。

中间件里怎么判断请求IP是否在白名单
直接读 $request->ip() 最不可靠——它可能被代理污染。真要校验,得从 $_SERVER['REMOTE_ADDR'] 拿原始 IP,再结合 TRUSTED_PROXIES 配置决定是否信任 X-Forwarded-For。Laravel 默认不自动解析可信代理头,不配就永远拿不到真实用户IP。
- 检查
config/trustedproxy.php是否存在,没有就运行php artisan vendor:publish --provider="Fideloper\Proxy\TrustedProxyServiceProvider" - 把负载均衡或 Nginx 的真实出口 IP 加到
TRUSTED_PROXIES环境变量里,比如10.0.0.5,10.0.0.6 - 在中间件中用
$request->ip()(不是$request->getClientIp())——Laravel 修过这个方法,它会在可信代理配置生效后自动回溯
Laravel 10+ 白名单中间件的最小实现
别套模板,写个干净的 CheckIpWhitelist 类就行。重点是早拦截、不依赖路由参数、兼容 CLI 请求(比如队列任务触发的 HTTP 调用)。
- 构造函数里读白名单,推荐从配置文件取:
config('app.ip_whitelist', []),别硬编码数组 - 在
handle()开头就做判断,匹配不上立刻return response('', 403),别走完整流程 - 对
php artisan serve或本地开发环境,加个app()->environment('local')短路逻辑,避免调试时反复改白名单 - 注意:CLI 请求的
$request->ip()是127.0.0.1,如果白名单没包含它,命令行跑测试会失败
白名单配置放哪最合适
放 config/app.php 里太乱,放 .env 又难写数组。最稳的是单独建 config/ip-whitelist.php:
return [
'allowed' => explode(',', env('IP_WHITELIST', '127.0.0.1')),
];
然后在中间件里用 config('ip-whitelist.allowed')。这样既支持环境变量注入(IP_WHITELIST=192.168.1.10,203.0.113.5),也方便测试时直接改 PHP 数组。
- 别用
file_get_contents动态读 JSON 或 INI——启动时加载比运行时 IO 更快更稳 - 如果白名单超 50 个 IP,考虑用 CIDR 格式(如
192.168.1.0/24),但 Laravel 原生不支持,得自己写ip_in_range()辅助函数 - 线上环境禁止把白名单配置提交到 Git,用
.env或 secrets manager 管理
为什么 nginx 层做白名单更靠谱
PHP 中间件拦不住扫描器暴力请求,CPU 和内存早被占满。真正高并发场景下,白名单必须下沉到 Web 服务器层。
- Nginx 里用
geo指令预编译 IP 列表,比每次进 PHP 解析快一个数量级 - 配合
map和return 403,请求根本不会打到 PHP-FPM - 但要注意:Nginx 的
$remote_addr默认是上一级代理 IP,需确保real_ip_header X-Forwarded-For和set_real_ip_from配置正确 - 开发和生产配置不一致?建议在 Laravel 中间件留个兜底开关(比如环境变量
WHITELIST_MODE=nginx|php),避免上线后才发现漏配
IP 白名单真正的复杂点不在代码怎么写,而在于你是否清楚当前流量路径里到底有几层代理、每层是否转发了原始 IP、以及运维是否愿意配合改 Nginx 配置。没摸清这两点,光写中间件只是自我安慰。











