curl_init是PHP发HTTP请求唯一靠谱起点,file_get_contents不支持自动跳转、Cookie管理和HTTPS证书验证;$_POST仅解析特定Content-Type,JSON需读php://input;状态码须与header配对使用;防盗链不可依赖HTTP_REFERER,跨域需正确配置CORS头。

直接用 curl_init 发请求,别碰 file_get_contents 做 POST
PHP里发HTTP请求,curl_init 是唯一靠谱的起点。很多人图省事用 file_get_contents 拼 stream_context_create 发POST,结果遇到重定向失败、Cookie不携带、HTTPS证书报错就卡住——它底层不支持自动跳转和完整会话管理,连 Location 头都懒得跟。
实操建议:
-
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1)必开,否则302跳转会直接返回空响应 - POST必须显式设
CURLOPT_POST为1,不能只靠CURLOPT_POSTFIELDS - 传JSON时,
CURLOPT_HTTPHEADER要包含"Content-Type: application/json",且CURLOPT_POSTFIELDS得是json_encode($data)后的字符串 - HTTPS请求务必加
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0)和curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0)(仅开发环境),否则自签名或过期证书直接curl_exec返回false
$_GET 和 $_POST 不是“接收HTTP数据”的全部
初学者常以为表单一提交,$_POST 就自动有值——其实它只解析 Content-Type: application/x-www-form-urlencoded 或 multipart/form-data 的请求体。如果前端用 fetch 发 application/json,$_POST 是空的,得手动读 php://input。
常见错误现象:
立即学习“PHP免费学习笔记(深入)”;
- Vue/React调后端API返回空数组,查
$_POST是空的,但file_get_contents('php://input')能拿到原始JSON - 用
curl -H "Content-Type: application/json" -d '{"a":1}'测试,$_POST仍为空
正确做法:先判断 $_SERVER['CONTENT_TYPE'] 是否含 application/json,再决定读 php://input 并 json_decode。
状态码不是装饰,http_response_code 和 header 得配对用
写接口时只 echo JSON,却不设状态码,前端 fetch 的 response.ok 永远是 true——因为PHP默认返回200,哪怕你逻辑出错返回了 {"error":"not found"}。
参数差异:
-
http_response_code(404)只改响应行的数字,不发header(),某些Nginx配置下会被覆盖回200 - 安全写法是
http_response_code(404); header('Content-Type: application/json');,确保状态码和头都生效 - 500类错误别依赖
try/catch后硬设200,应让未捕获异常触发PHP默认500,或显式http_response_code(500)
防盗链和跨域不是同一回事,$_SERVER['HTTP_REFERER'] 别当真
用 $_SERVER['HTTP_REFERER'] 做防盗链?浏览器可随意伪造这个头,curl、Postman、甚至地址栏直接访问都绕过。真正防资源盗用得靠服务端签名校验(如时间戳+密钥)或Referer白名单配合CDN规则。
而跨域(CORS)是浏览器强制的同源策略,PHP要响应 Access-Control-Allow-Origin 头才能让JS请求成功。容易踩的坑:
- 前端带
credentials: true,后端没加Access-Control-Allow-Credentials: true,请求直接被浏览器拦截 - 允许所有域名用
Access-Control-Allow-Origin: *,但一旦开了凭证,就必须指定具体域名,不能用* - 预检请求(OPTIONS)没处理,导致带自定义头的请求根本到不了你的PHP脚本
复杂点在于:防盗链是业务层的事,跨域是协议层的事,混着处理只会让逻辑越来越脆。该签名校验的签,该配CORS头的配,别指望一个 if ($_SERVER['HTTP_REFERER']) 解决所有问题。



















