Для того чтобы отличить прямой и AJAX-запрос достаточно проверить значение глобальной переменной $_SERVER['HTTP_X_REQUESTED_WITH']
.
<?php
if (@$_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
// Обработка AJAX запроса...
}
Использование данного метода в целях безопасности не имеет ни какого смысла т.к. серверу можно передать любые заголовки, например через cURL.
X-Requested-With - пользовательский HTTP-заголовок. Он не определён никакими стандартами. Когда вы делаете AJAX-запрос современными JavaScript-методами fetch() или new XMLHttpRequest(), этот заголовок отсутствует - если только вы не передали его явно в своём запросе. С равным успехом вы можете передать любой другой заголовок (X-By-Vasya) и ловить его на строное PHP.
Не существует способа надёжно детектировать AJAX-запрос, ничего не зная о запрашивающем скрипте. В теории наиболее правильный способ - ловить заголовок "Accept". Если его значение равно "application/json", можно с вероятностью 100% утверждать, что это либо AJAX-запрос, либо API-запрос. Однако и этот заголовок может отсутствовать, если кодер поленился его задать.