如何判断PHP脚本是AJAX调用还是浏览器访问?原方法失效求替代方案
我完全理解你的困扰——本来依赖X-Requested-With: XMLHttpRequest这个请求头是区分AJAX和直接访问的常规操作,但在特定环境下失效确实让人头疼,而且你还不想额外加GET/POST参数,这个需求非常合理。
首先,我们得先排查下为什么原本的方法在PHP 7.1.11 + Chrome 63环境下失效,常见原因有这几个:
- 前端请求未自动添加该头:只有jQuery、Axios这类封装好的AJAX库会自动设置
X-Requested-With头,原生XMLHttpRequest或fetchAPI默认是不会加的。如果你的前端用的是原生请求,需要手动调用setRequestHeader('X-Requested-With', 'XMLHttpRequest')才行。 - 服务器配置过滤了请求头:比如Apache或Nginx的配置中,可能没有把这个自定义头传递给PHP的
$_SERVER变量。你可以先在PHP里打印var_dump($_SERVER),看看有没有HTTP_X_REQUESTED_WITH这个键,确认是否是服务器端的问题。 - 浏览器/插件限制:Chrome 63的某些隐私设置或第三方插件可能会阻止这类自定义请求头的发送,不过这种情况相对少见。
接下来,给你几个无需额外GET/POST参数的替代区分方案:
1. 检查Accept请求头
AJAX请求通常会明确指定接受的数据类型(比如JSON、XML),而浏览器直接访问一般默认接受text/html。你可以在PHP中这样判断:
$isAjax = false; if (!empty($_SERVER['HTTP_ACCEPT'])) { // 匹配常见的AJAX接受类型 $ajaxAcceptTypes = ['application/json', 'application/xml', 'text/json', 'text/xml']; foreach ($ajaxAcceptTypes as $type) { if (strpos($_SERVER['HTTP_ACCEPT'], $type) !== false) { $isAjax = true; break; } } }
⚠️ 注意:这个方法不是100%绝对(比如有些AJAX也可能请求HTML,或者浏览器插件会修改Accept头),但结合业务场景可以作为有效辅助。
2. 自定义专属请求头(最可靠)
如果你能控制前端的AJAX请求逻辑,可以自定义一个专属的请求头(不用加在URL或POST参数里),比如X-Is-Ajax: 1,然后在PHP中检查:
$isAjax = isset($_SERVER['HTTP_X_IS_AJAX']) && $_SERVER['HTTP_X_IS_AJAX'] === '1';
前端原生XMLHttpRequest的设置示例:
const xhr = new XMLHttpRequest(); xhr.open('GET', 'your-script.php'); xhr.setRequestHeader('X-Is-Ajax', '1'); xhr.send();
这种方式完全不受环境影响,是最稳定的区分方法,而且符合你“无需额外参数”的要求。
3. 结合HTTP_REFERER辅助判断
如果是同域下的AJAX请求,HTTP_REFERER通常会指向发起请求的页面URL;而直接访问脚本时,HTTP_REFERER大概率为空(或者不是本站域名)。你可以这样辅助验证:
$isSameDomainReferer = false; if (!empty($_SERVER['HTTP_REFERER'])) { $refererHost = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); $currentHost = parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST); $isSameDomainReferer = ($refererHost === $currentHost); } // 结合Accept头或其他条件一起判断 $isAjax = $isSameDomainReferer && strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false;
⚠️ 注意:HTTP_REFERER可以被浏览器或用户手动修改,所以只能作为辅助条件,不能单独依赖。
4. 检查请求的Content-Type(针对POST请求)
如果你的AJAX是POST请求,通常会使用application/json或application/x-www-form-urlencoded(后者和表单提交一致,但前者是AJAX常用的)。可以这样判断:
$isAjaxPost = false; if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_SERVER['HTTP_CONTENT_TYPE'])) { $isAjaxPost = strpos($_SERVER['HTTP_CONTENT_TYPE'], 'application/json') !== false; }
综合来看,自定义专属请求头是最推荐的方案,只要能控制前端,就能100%可靠地区分;如果无法修改前端,就结合Accept头、Referer等多个条件来提高判断的准确性。
内容的提问来源于stack exchange,提问作者Mawg

