file_get_contents带stream_context_create易被限流,因默认User-Agent为PHP/8.x、无Cookie复用、连接不复用、请求间隔随机,服务端识别为脚本刷量,触发Nginx limit_req或Redis计数等限流策略。

为什么 file_get_contents 带 stream_context_create 容易被限流?
因为默认 User-Agent 是 PHP/8.x,且无 Cookie 复用、连接不复用、请求间隔随机——服务端一眼识别为脚本刷量。限流策略(如 Nginx 的 limit_req 或后端 Redis 计数)会优先拦截这类“无状态高频请求”。
- 必须显式设置
User-Agent和Accept,接近真实浏览器(例如Mozilla/5.0 (Windows NT 10.0; Win64; x64)) - 若接口依赖会话,需手动维护
Cookie字符串,不能靠内置 session -
file_get_contents每次都是新 TCP 连接,高并发下易触发连接数限制,改用cURL可复用CURLOPT_HTTPHEADER+CURLOPT_COOKIE+CURLOPT_TCP_KEEPALIVE
用 cURL 模拟 POST 并绕过基础限流的最小可行配置
重点不是“伪装”,而是让请求行为更像人:带 Referer、控制节奏、复用连接、容忍 429。
- 启用
CURLOPT_RETURNTRANSFER,禁用CURLOPT_HEADER(除非调试需要) - 设
CURLOPT_REFERER为该接口所属域名首页(如https://api.example.com/) - 加
CURLOPT_HTTPHEADER:包括Content-Type: application/json(按接口要求)、Accept: application/json、X-Requested-With: XMLHttpRequest - 关键:设置
CURLOPT_CONNECTTIMEOUT_MS为 3000,CURLOPT_TIMEOUT_MS为 8000,避免因超时重试放大请求压力 - 发送 JSON 数据时,用
json_encode($data)后传给CURLOPT_POSTFIELDS,别漏掉CURLOPT_POST= true
遇到 429 Too Many Requests 怎么办?
这不是失败,是明确信号:你已被速率控制器盯上。硬重试只会延长封禁时间。
- 检查响应头中的
Retry-After字段,有值就sleep((int)$retry_after),没值则至少sleep(1) - 不要在循环里裸写
curl_exec(),应封装成带退避的函数,例如首次 1s,失败后 2s、4s、8s(指数退避) - 若接口返回 JSON 含
"code":429或类似字段,也按 429 处理,别只看 HTTP 状态码 - 记录每次请求的
microtime(true)和返回码,用于后续分析是否真被限——有时是后端服务降级返回 429,而非限流
真正影响限流效果的隐藏变量
很多人调通了请求就以为搞定,但第二天又被封,问题常出在这些地方:
立即学习“PHP免费学习笔记(深入)”;
- IP 出口不稳定:如果跑在云函数或共享代理池,IP 被连坐封禁,得固定出口或轮换代理
- DNS 缓存:PHP 默认不缓存 DNS,高频请求可能卡在
gethostbyname,加CURLOPT_DNS_CACHE_TIMEOUT= 300 - SSL 握手开销:对 HTTPS 接口,开启
CURLOPT_SSL_VERIFYPEER= false(仅测试环境!)或复用CURLOPT_SSL_SESSIONID_CACHE= true - POST body 太大或含敏感字段(如
password、token)可能触发 WAF 主动限流,先用最小 payload 测试基线
限流逻辑永远在服务端演进,能稳定跑一周的脚本,下周可能失效——留好日志、监控返回头、把 Retry-After 和 X-RateLimit-Remaining 当作必读字段,比调参重要得多。











