Работа с заголовками HTTP запроса: получение данных средствами PHP
Получение HTTP заголовков запроса в PHP
Как наиболее эффективно получить все заголовки HTTP запроса в PHP?
Основное решение: использование функции getallheaders(), доступной начиная с PHP 7.0. Она возвращает ассоциативный массив, где ключи - имена заголовков, а значения - их содержимое. Функция работает в режиме модуля Apache и FastCGI (через FPM).
$headers = getallheaders();
print_r($headers);
Php get request headers (получение заголовков http запроса в php)
Результат (пример для запроса с заголовками Accept, User-Agent, Host, Accept-Language):
Array
(
[Host] => example.com
[User-Agent] => Mozilla/5.0
[Accept] => text/html
[Accept-Language] => ru-RU
)
Возможная проблема: если PHP работает в режиме CLI или под Nginx без FastCGI, функция может быть недоступна. В таких случаях возникает ошибка "Call to undefined function". Решение: проверить наличие функции через function_exists('getallheaders') и использовать альтернативный метод.
Как получить заголовки запроса в среде, где getallheaders() недоступна?
Можно использовать глобальный массив $_SERVER. Все HTTP заголовки от клиента попадают туда с префиксом HTTP_ (заголовки). Имена преобразуются в верхний регистр, дефисы заменяются на подчеркивания. Например, Content-Type становится HTTP_CONTENT_TYPE.
$headers = [];
foreach ($_SERVER as $key => $value) {
if (strpos($key, 'HTTP_') === 0) {
$headerName = str_replace('_', '-', substr($key, 5));
$headerName = implode('-', array_map('ucfirst', explode('-', strtolower($headerName))));
$headers[$headerName] = $value;
}
}
print_r($headers);
Этот код восстанавливает исходные имена заголовков (например, Accept-Encoding). Без преобразования имена будут в верхнем регистре с подчеркиваниями.
Типичная ошибка: некоторые заголовки (например, Content-Type, Content-Length) могут не иметь префикса HTTP_. Для них доступны отдельные ключи CONTENT_TYPE и CONTENT_LENGTH (без префикса). Их также нужно обрабатывать отдельно.
Улучшенный вариант с включением специальных заголовков:
$specialHeaders = ['CONTENT_TYPE', 'CONTENT_LENGTH'];
$headers = [];
foreach ($_SERVER as $key => $value) {
if (strpos($key, 'HTTP_') === 0) {
$headerName = str_replace('_', '-', substr($key, 5));
$headerName = implode('-', array_map('ucfirst', explode('-', strtolower($headerName))));
$headers[$headerName] = $value;
} elseif (in_array($key, $specialHeaders)) {
$headerName = str_replace('_', '-', $key);
$headerName = implode('-', array_map('ucfirst', explode('-', strtolower($headerName))));
$headers[$headerName] = $value;
}
}
Как извлечь только один конкретный заголовок, например Authorization?
Если требуется проверить наличие и значение одного заголовка, удобно напрямую обратиться к $_SERVER или использовать фильтр с проверкой.
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? null;
if ($authHeader) {
echo "Authorization: $authHeader";
} else {
echo "Authorization header is missing";
}
Альтернатива - взять из массива, полученного одним из способов выше.
Проблема: заголовок Authorization может быть передан не в $_SERVER['HTTP_AUTHORIZATION'], а в $_SERVER['HTTP_AUTHORIZATION'] или быть пустым при определённых настройках сервера (особенно в FastCGI). На практике потребуется проверка нескольких вариантов.
$auth = $_SERVER['HTTP_AUTHORIZATION']
?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
?? '';
Как использовать apache_request_headers() для совместимости со старыми версиями PHP?
В PHP 5.x существовала только apache_request_headers(). Она доступна при модуле Apache. В PHP 7.0+ getallheaders() является её псевдонимом, поэтому обе функции взаимозаменяемы. Для кода, работающего в разных окружениях, можно создать обёртку.
if (function_exists('getallheaders')) {
$headers = getallheaders();
} elseif (function_exists('apache_request_headers')) {
$headers = apache_request_headers();
} else {
$headers = [];
// резервный сбор из $_SERVER как выше
}
Такой подход гарантирует работу как в Apache, так и в других SAPI.
Как получить заголовки запроса при обращении через командную строку (CLI)?
При запуске скрипта из командной строки HTTP запросов нет, поэтому $_SERVER не содержит HTTP заголовков. Однако для тестирования можно передавать заголовки через аргументы командной строки или переменные окружения. Пример передачи через окружение:
// command line: X-Forwarded-For=127.0.0.1 php script.php
$headers = getenv('X-Forwarded-For') ? ['X-Forwarded-For' => getenv('X-Forwarded-For')] : [];
print_r($headers);
Для полноценного тестирования лучше использовать встроенный веб-сервер PHP: php -S localhost:8080.
Расширенные примеры работы с заголовками запроса
Ниже приведены примеры, которые демонстрируют более сложные сценарии: фильтрация, сериализация, логирование и интеграция с другими компонентами.
Пример 1. Фильтрация заголовков по префиксу
Этот пример извлекает только заголовки, начинающиеся с X- (пользовательские заголовки). Это полезно для обработки кастомных метаданных.
$allHeaders = getallheaders();
$xHeaders = array_filter($allHeaders, function($key) {
return strpos($key, 'X-') === 0;
}, ARRAY_FILTER_USE_KEY);
print_r($xHeaders);
Array
(
[X-Request-Id] => abc123
[X-Custom] => value
)
Пример 2. Преобразование всех заголовков в JSON для логирования
При отладке или аудите удобно сохранять заголовки в формате JSON.
$headers = getallheaders();
$jsonHeaders = json_encode($headers, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
error_log("Request headers: " . $jsonHeaders);
echo $jsonHeaders;
{
"Host": "example.com",
"User-Agent": "Mozilla/5.0",
"Accept": "text/html"
}
Пример 3. Проверка наличия обязательного заголовка с выдачей ошибки
В API часто требуется проверка заголовка Content-Type или Authorization.
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (stripos($contentType, 'application/json') === false) {
http_response_code(415);
echo json_encode(['error' => 'Unsupported Media Type']);
exit;
}
echo 'Content-Type validated successfully.';
Пример 4. Извлечение заголовков с использованием cURL в роли клиента
Иногда PHP выступает в роли HTTP клиента и отправляет запросы. Пример получения заголовков ответа (не запроса) с помощью cURL.
$ch = curl_init('https://api.example.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headerStr = substr($response, 0, $headerSize);
$headers = explode("\r\n", trim($headerStr));
print_r($headers);
curl_close($ch);
Array
(
[0] => HTTP/1.1 200 OK
[1] => Content-Type: application/json
[2] => Date: Mon, 01 Jan 2024 00:00:00 GMT
...
)
Пример 5. Обработка заголовков с одинаковыми именами
Некоторые заголовки (например, Set-Cookie) могут повторяться. getallheaders() в этом случае возвращает только последнее значение. Для получения всех значений требуется парсинг сырых заголовков.
$rawHeaders = getallheaders();
// getallheaders не поддерживает множественные значения, поэтому используем $_SERVER с осторожностью
// Альтернативный подход: получить строку заголовков через apache_response_headers()? Нет, это для ответа.
// Для запроса множественные значения можно получить через explode на основе
// but PHP не сохраняет исходный порядок. Лучше использовать модуль Apache или Nginx с сохранением.
// Простой неполный пример: искать через preg_match_all на сыром HTTP запросе (только если доступен)
// Однако в рамках PHP это сложно. Обычно рекомендуется обрабатывать как единую строку.
При необходимости работы с двойными заголовками (например, Cookie) стоит применить ручной разбор $_SERVER с учётом возможных дубликатов (через array_count_values и array_keys).
Пример 6. Создание обёртки для получения заголовков с автодополнением недостающих
Пользовательская функция, которая объединяет все источники и гарантированно возвращает массив.
function getRequestHeaders(): array {
if (function_exists('getallheaders')) {
return getallheaders();
}
if (function_exists('apache_request_headers')) {
return apache_request_headers();
}
$headers = [];
$specialHeaders = ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) === 'HTTP_') {
$headerName = str_replace('_', '-', substr($key, 5));
$headerName = implode('-', array_map('ucfirst', explode('-', strtolower($headerName))));
$headers[$headerName] = $value;
} elseif (in_array($key, $specialHeaders)) {
$headers[$key] = $value;
}
}
return $headers;
}
$headers = getRequestHeaders();
var_dump($headers);
array(4) {
["Host"] => string(11) "example.com"
["User-Agent"] => string(10) "Mozilla/5.0"
["Accept"] => string(9) "text/html"
["Content-Type"] => string(9) "text/html"
}
Эта обёртка подходит для большинства окружений и легко расширяема.