Отправка HTTP-заголовков: полное руководство по PHP

Раздел: Веб-разработка -> Работа с HTTP

Отправка HTTP-заголовков в PHP: функция header() и её особенности

Основное решение: функция header()

Для отправки произвольного HTTP-заголовка в PHP используется встроенная функция header(). Она принимает строку с заголовком и необязательные параметры. Вызов должен происходить до любого вывода в браузер (включая пробелы и HTML).

<?php
header('Content-Type: text/html; charset=utf-8');
echo 'Привет, мир!';
?>

В этом примере устанавливается заголовок Content-Type с указанием кодировки UTF-8. После вызова header() можно отправлять тело ответа.

Как выполнить перенаправление (редирект) на другую страницу?

Для редиректа используется заголовок Location с указанием нового URL. Функция header() вызывается с этим заголовком, после чего выполнение скрипта обычно завершается вызовом exit().

<?php
header('Location: https://example.com/new-page');
exit;
?>

Важно: после header('Location: ...') следует вызывать exit или die, чтобы скрипт не продолжал выполнение. Иначе может быть отправлен лишний контент.

Типичная ошибка

Если перед вызовом header() уже был отправлен какой-либо вывод (например, пробел в начале файла или echo), произойдет ошибка "Cannot modify header information - headers already sent". Решение: рекомендуется проверить, что нет вывода до header(), или использовать буферизацию вывода (ob_start()).

Как установить код состояния HTTP-ответа?

Для установки кода состояния используется третий параметр функции header() - http_response_code. Можно также воспользоваться функцией http_response_code().

<?php
header('HTTP/1.0 404 Not Found');
// или
http_response_code(404);
?>

Первый способ задает строку состояния напрямую, второй - более современный и удобный.

Как изменить или заменить существующий заголовок?

По умолчанию header() заменяет предыдущий заголовок с тем же именем. Второй параметр (replace) позволяет управлять этим: если передать false, то будет добавлен ещё один заголовок (например, несколько заголовков Set-Cookie).

<?php
header('Set-Cookie: name=value', false); // второй такой же заголовок не заменит первый
?>

Как отправить заголовок без замены (добавить несколько одинаковых)?

Для отправки нескольких заголовков с одним именем (например, Set-Cookie или Link) используется второй параметр false.

Как удалить ранее отправленный заголовок?

Функция header_remove() позволяет удалить ранее отправленный заголовок (если вывод еще не начался). Можно удалить конкретный заголовок по имени или все.

<?php
header('X-Custom: value');
// позже
header_remove('X-Custom'); // удалит этот заголовок
?>

Как проверить, какие заголовки уже отправлены?

Функция headers_list() возвращает массив всех заголовков, которые были установлены (но ещё не отправлены, если вывод не начался). headers_sent() проверяет, началась ли отправка заголовков.

<?php
var_dump(headers_list());
var_dump(headers_sent());
?>

Проблема: заголовки не отправляются, если был вывод.

Решение: использовать буферизацию вывода (ob_start() в начале скрипта) или убедиться, что header() вызывается до любого вывода. Также можно проверить наличие BOM в файлах.

Как запретить кэширование страницы браузером?

Для управления кэшированием используются заголовки Cache-Control, Pragma, Expires.

<?php
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
?>

Эти три заголовка гарантируют, что браузер не сохранит страницу в кэше.

Как организовать принудительную загрузку файла (Content-Disposition)?

Заголовок Content-Disposition с аттачментом указывает браузеру скачать файл.

<?php
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="document.pdf"');
header('Content-Length: ' . filesize('document.pdf'));
readfile('document.pdf');
?>

Важно указать правильный MIME-тип и размер файла.

Как настроить CORS-заголовки для кросс-доменных запросов?

Для разрешения запросов с других доменов отправляется заголовок Access-Control-Allow-Origin.

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
?>

При сложных запросах (preflight) необходимо обрабатывать OPTIONS-запрос.

Как реализовать базовую HTTP-аутентификацию?

Заголовок WWW-Authenticate заставляет браузер показать диалог логина и пароля.

<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="My Realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Требуется авторизация';
    exit;
} else {
    echo 'Привет, ' . htmlspecialchars($_SERVER['PHP_AUTH_USER']);
}
?>

Как отправить несколько cookie с помощью setcookie()?

Функция setcookie() внутри использует header() для установки заголовка Set-Cookie. Для отправки нескольких cookie можно вызвать setcookie() несколько раз.

<?php
setcookie('user', 'Alice', time()+3600);
setcookie('theme', 'dark', time()+3600);
?>

Расширенные примеры отправки HTTP-заголовков в PHP

Пример 1: Перенаправление с разными кодами состояния

Пример
<?php
// 301 Moved Permanently
header('HTTP/1.1 301 Moved Permanently');
header('Location: https://example.com/new');
exit;
?>

Результат: браузер получит статус 301 и перейдет на новый URL.

Пример 2: Установка произвольных заголовков и проверка списка

Пример
<?php
header('X-Custom-Header: value1');
header('X-Custom-Header: value2', false); // добавить второй
$headers = headers_list();
print_r($headers);
?>

Результат (вывод на экран):

Array
(
    [0] => X-Custom-Header: value1
    [1] => X-Custom-Header: value2
)

Пример 3: Скачивание файла с динамическим именем

Пример
<?php
$file = 'report.pdf';
$filename = basename($file);
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
?>

Результат: браузер предложит сохранить файл с именем report.pdf.

Пример 4: Обработка preflight запроса CORS

Пример
<?php
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
    header('Access-Control-Allow-Headers: Content-Type, Authorization');
    header('Access-Control-Max-Age: 86400');
    header('Content-Length: 0');
    header('Content-Type: text/plain');
    http_response_code(204);
    exit;
}
// обычный ответ
header('Access-Control-Allow-Origin: *');
echo 'OK';
?>

Результат: браузер получит статус 204 для OPTIONS и сможет выполнить кросс-доменный запрос.

Пример 5: Управление кэшем через ETag и Last-Modified

Пример
<?php
$etag = md5(file_get_contents('data.txt'));
header('ETag: "' . $etag . '"');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime('data.txt')) . ' GMT');
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == '"' . $etag . '"') {
    http_response_code(304);
    exit;
}
// иначе отдаем контент
echo file_get_contents('data.txt');
?>

Результат: если содержимое не изменилось, возвращается статус 304 Not Modified.

Пример 6: Отправка JSON с заголовками

Пример
<?php
$data = ['status' => 'success', 'message' => 'Данные получены'];
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE);
?>

Результат: браузер получит JSON-строку с русскими символами.

Пример 7: Использование буферизации для заголовков после вывода

Пример
<?php
ob_start(); // включить буферизацию
echo 'Этот текст будет выведен до заголовков';
header('X-Some: value'); // работает, т.к. вывод буферизован
ob_end_flush(); // отправить буфер и заголовки
?>

Результат: заголовок X-Some будет установлен, несмотря на предшествующий вывод.

Отправка HTTP-заголовков - comments

En
Start php header (php)