Работа с заголовками HTTP запроса: получение данных средствами PHP

Раздел: Работа с HTTP заголовками -> Чтение заголовков запроса

Получение 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"
}

Эта обёртка подходит для большинства окружений и легко расширяема.

Получение заголовков HTTP запроса в PHP - comments

En
Php get request headers (php)