为何GET请求下file_get_contents('php://input')返回NULL?
背景
服务器迁移至**CloudLinux v9.5.0(搭配cPanel 126.0.9)**后,使用Postman发送原始JSON数据时,仅GET请求的file_get_contents('php://input')返回NULL,POST、PUT、PATCH请求均正常。迁移前GET请求处理完全正常。
测试脚本:
<?php echo strtoupper($_SERVER['REQUEST_METHOD']) . "\n\n\n"; $data = json_decode(file_get_contents('php://input'), true); var_dump($data); ?>
1. CloudLinux v9.5.0是否存在阻止GET请求携带请求体的配置变更?
CloudLinux本身默认没有这类限制,但它搭配的cPanel(尤其是126版本的EasyApache 4)或默认的Apache/Nginx配置可能做了调整。HTTP规范并未强制禁止GET请求带请求体,但很多服务器默认会忽略或剥离GET的请求体,cPanel的EA4默认配置可能新增了这类处理逻辑。
2. 是否有mod_security规则或PHP设置会剥离GET请求的请求体?
mod_security
大概率是ModSecurity的规则拦截了GET请求的请求体。OWASP核心规则集(CRS)里有针对GET请求带请求体的异常检测规则,会直接截断请求体。可以通过以下方式排查:
- 查看ModSecurity日志(路径通常为
/var/log/apache2/modsec_audit.log,或在cPanel的「ModSecurity」模块中查看),搜索目标请求的ID,看是否有拦截记录。 - 临时禁用ModSecurity测试,若请求正常,则确定是规则问题,可针对性禁用相关规则。
PHP设置
PHP本身的配置(如php.ini中的enable_post_data_reading)仅影响POST请求,GET请求的php://input不受该参数控制。但如果使用PHP-FPM,FastCGI的配置可能会丢弃GET请求体:
- Apache环境:检查虚拟主机配置中是否有
ProxyFCGISetEnvIf相关规则,导致请求体未传递给PHP-FPM。 - Nginx环境:检查location配置中是否有
fastcgi_pass_request_body off;这类禁用请求体传递的设置。
3. 是否应该用$_GET替代php://input处理GET请求?
不建议。$_GET解析的是URL中的查询参数,而php://input读取的是请求体内容,二者逻辑完全不同。强行用$_GET需要将JSON嵌入URL参数,会带来以下问题:
- 违反REST语义规范,GET请求的查询参数应用于过滤、排序而非传递业务数据。
- URL长度有限制(不同浏览器/服务器上限不同),无法传递较大的JSON数据。
- 破坏你原本统一处理各请求方法请求体的逻辑。
4. 有没有办法让GET请求可以携带JSON请求体?
有以下几种可行方案:
方案1:调整服务器配置
- ModSecurity:找到拦截GET请求体的规则,在cPanel的ModSecurity模块中添加规则例外,或直接禁用该规则。
- Apache + PHP-FPM:在虚拟主机配置中添加以下配置,确保GET请求的请求体被传递:
同时确保SetEnvIf Request_Method "GET" HTTP_CONTENT_LENGTH=$HTTP_CONTENT_LENGTH SetEnvIf Request_Method "GET" HTTP_CONTENT_TYPE=$HTTP_CONTENT_TYPEProxyFCGI配置没有丢弃请求体。 - Nginx + PHP-FPM:在处理PHP请求的location块中添加:
移除任何针对GET请求禁用请求体的配置。fastcgi_pass_request_body on; fastcgi_pass_request_headers on;
方案2:调整请求方式
如果服务器配置修改困难,可将需要携带JSON体的GET请求改为POST或PUT(虽然不符合GET的语义,但能快速解决问题);或者使用POST请求并通过_method=GET参数模拟GET(需后端框架支持方法覆盖)。
方案3:原始请求数据读取(不推荐)
可以尝试读取原始HTTP请求数据来获取请求体,比如使用$GLOBALS['HTTP_RAW_POST_DATA'](但该变量已在PHP 7.0+废弃),或通过Apache的mod_cgi环境变量读取,但这种方式兼容性差,仅作为最后手段。
额外排查步骤
- 使用curl测试,排除Postman的问题:
curl -X GET -H "Content-Type: application/json" -d '{"test": 123}' http://your-domain/test.php - 查看服务器访问日志,确认请求的
Content-Length头是否正确传递给了后端。 - 在cPanel的「EasyApache 4」模块中,检查是否启用了针对GET请求的特殊过滤组件。
内容的提问来源于stack exchange,提问作者zcool

