最可靠的方式是在TrustProxies中间件中配置可信代理并支持CIDR白名单,同时记录拒绝日志、区分API/Web响应、逐层校验转发头。

中间件里用 $request->ip() 获取真实 IP 最可靠
很多 Laravel 项目直接用 $request->ip() 却发现返回的是反向代理(如 Nginx、Cloudflare)的内网地址,比如 127.0.0.1 或 10.0.0.2。这是因为 Laravel 默认不信任代理头,得手动配置信任链。
实操建议:
- 在
App\Http\Middleware\TrustProxies中设置$proxies,例如:protected $proxies = ['127.0.0.1', '10.0.0.0/8']; - 同时确认
$headers包含Illuminate\Http\Request::HEADER_X_FORWARDED_FOR(默认已设好) - 别依赖
$_SERVER['REMOTE_ADDR']—— 它在代理后永远不准 - 如果用 Cloudflare,要额外加
'CF-Connecting-IP'到TrustProxies::$header(需自定义中间件或升级到 Laravel 9+ 的setTrustedProxies()方式)
写白名单中间件时,in_array() 别漏掉 CIDR 支持
硬编码写 in_array($ip, ['192.168.1.100', '203.0.113.5']) 看似简单,但实际运维中白名单常是网段(如 192.168.1.0/24),原生 PHP 不支持 CIDR 匹配。
实操建议:
- 用
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)先校验 IP 格式,避免字符串注入风险 - 判断网段推荐用
ip_in_range($ip, '192.168.1.0/24')这类辅助函数,Laravel 自带没有,得自己写或引入轻量包(如spatie/ip-address) - 白名单数组建议从配置文件读取(
config('firewall.whitelist')),别写死在中间件里 - 注意 IPv6 地址格式差异:
::1和2001:db8::1需单独处理,ip_in_range()类函数要确认是否支持
abort(403) 前先记录被拒 IP,否则排查时两眼一抹黑
只写 if (!in_whitelist($ip)) abort(403); 很干净,但线上突然有业务方说“连不上”,你根本不知道谁被拦了、为什么被拦。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
实操建议:
- 在拒绝前加日志:
Log::warning('IP blocked', ['ip' => $ip, 'url' => $request->fullUrl(), 'user_agent' => $request->userAgent()]); - 别用
info()或debug()—— 生产环境默认不记录,warning()更稳妥 - 如果用了队列或异步请求,
$request->ip()可能为空,得提前判空再记录 - 日志里别打敏感字段(如完整 token、手机号),IP 和路径已足够定位问题
API 路由和 Web 路由白名单逻辑不能混用
同一个中间件套在 api 和 web 路由组上,结果 API 接口因没 Session 被重定向到登录页,或者 Web 页面返回 JSON 错误——本质是响应格式错位。
实操建议:
- 拆成两个中间件:一个返回
abort(403)(适合 API),一个返回视图或重定向(适合 Web) - 检查路由分组是否启用
middleware('api'),它自带throttle和auth:sanctum,和 IP 白名单逻辑无直接关系,但叠加可能掩盖真实拦截点 - 测试时用
curl -H "X-Forwarded-For: 192.168.1.100" http://your.app/api/test模拟代理 IP,比改本地 hosts 更贴近真实链路
真正麻烦的不是写几行判断,而是当 CDN、WAF、K8s Ingress、Nginx 层层转发后,哪个头可信、哪个网段该放行、日志里看到的 IP 到底是哪一环填的——这些得对着每一层的配置去对,而不是只盯 Laravel 代码。










