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

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

Основы установки HTTP заголовков

Функция header() в PHP позволяет отправлять произвольные HTTP заголовки ответа. Вызов должен происходить до любого вывода данных (включая пробелы, HTML, echo). В противном случае возникает ошибка "headers already sent".

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

Http response code php (установка http статус кода в php)

Наиболее эффективное решение: проверка отправки заголовков

Для избежания ошибок рекомендуется перед вызовом header() проверять, не отправлены ли уже заголовки, с помощью функции headers_sent(). Также можно включить буферизацию вывода в начале скрипта.

<?php
ob_start(); // включение буферизации

// ... код ...

if (!headers_sent()) {
    header('Content-Type: application/json');
    echo json_encode($data);
} else {
    // если заголовки уже отправлены, можно очистить буфер
    ob_clean();
    header('Content-Type: text/plain');
    echo 'Ошибка: заголовки отправлены ранее';
}
ob_end_flush();
?>

Code php header (установка http заголовков в php)

Такой подход гарантирует, что заголовки будут установлены корректно, даже если в коде есть неконтролируемый вывод.

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

Для редиректа используется заголовок Location с последующим вызовом exit или die.

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

По умолчанию используется код состояния 302 (временный редирект). Для 301 (постоянный) нужно указать:

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

Типичные ошибки:

  • Вызов header() после вывода текста приводит к фатальной ошибке.
  • Пропуск exit позволяет продолжить выполнение скрипта, что может вызвать нежелательное поведение.
  • Использование относительного URL допускается, но рекомендуется абсолютный.

Как установить тип контента для скачивания файла?

Для принудительного скачивания файла используются заголовки Content-Type и Content-Disposition.

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

Возможные проблемы:

  • Неверный MIME-тип может вызвать некорректное отображение.
  • Отсутствие Content-Length может сломать прогресс загрузки.
  • Большие файлы требуют специальной обработки (буферизация, chunked).

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

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

<?php
// Отключение кэширования
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
header('Pragma: no-cache');
?>

Для разрешения кэширования на определенное время:

<?php
$seconds = 3600; // 1 час
header('Cache-Control: public, max-age=' . $seconds);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $seconds) . ' GMT');
?>

Распространенные ошибки:

  • Неправильное указание даты в Expires (должна быть в формате HTTP).
  • Смешивание Cache-Control и Pragma (последний устарел).
  • Игнорирование кэширования на стороне прокси.

Как настроить 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, Authorization');
?>

Для работы с авторизацией и куками требуется указать конкретный origin и установить Access-Control-Allow-Credentials: true.

Типичные проблемы:

  • Использование * вместе с Credentials: true запрещено.
  • Заголовки CORS должны быть отправлены даже для предварительных OPTIONS запросов.
  • Необходимость обработки preflight запросов.

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

HTTP заголовки безопасности помогают предотвратить атаки XSS, clickjacking и другие.

<?php
header('X-Frame-Options: DENY'); // запрет фреймов
header('X-Content-Type-Options: nosniff'); // запрет MIME sniffing
header('Strict-Transport-Security: max-age=31536000; includeSubDomains'); // HSTS
header('Content-Security-Policy: default-src \'self\''); // политика контента
?>

Ошибки и нюансы:

  • Слишком строгая CSP может сломать функциональность сайта.
  • HSTS требует HTTPS и не работает при первом визите.
  • X-Frame-Options устарел в пользу CSP frame-ancestors.

Как обработать ошибку "Headers already sent"?

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

<?php
ob_start(); // в начале скрипта
// ... код, который может выводить данные ...
if (!headers_sent()) {
    header('Content-Type: text/plain');
} else {
    ob_clean(); // очищаем буфер
    header('Content-Type: text/plain');
}
// вывод
ob_end_flush();
?>

Другие причины ошибки: пробелы перед <?php, BOM в файле, вывод до header.

Как найти источник вывода:

  • Включить отладку: error_reporting(E_ALL); ini_set('display_errors', 1);
  • Проверить наличие пробелов и пустых строк вначале файлов.
  • Использовать var_dump(headers_list()) для просмотра уже отправленных заголовков.

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

Данные примеры демонстрируют более сложные сценарии использования функции header().

Пример 1: Условное кэширование с ETag

Пример
<?php
$etag = md5(file_get_contents('content.txt'));
header('ETag: "' . $etag . '"');
header('Cache-Control: public, max-age=3600');
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == '"' . $etag . '"') {
    header('HTTP/1.1 304 Not Modified');
    exit;
}
readfile('content.txt');
?>

Результат:

При повторном запросе сервер вернет 304 Not Modified без тела, если контент не изменился.

Пример 2: Отправка нескольких кук с атрибутами

Пример
<?php
$cookieValue1 = 'session123';
$cookieValue2 = 'prefs_dark';
header('Set-Cookie: session=' . urlencode($cookieValue1) . '; Path=/; Secure; HttpOnly; SameSite=Strict');
header('Set-Cookie: preferences=' . urlencode($cookieValue2) . '; Path=/; Expires=' . gmdate('D, d M Y H:i:s', time() + 86400*30) . ' GMT; HttpOnly');
?>

Результат:

Браузер установит две куки с разными сроками жизни и атрибутами безопасности. Первая кука (сессионная) будет удалена при закрытии браузера, вторая сохранится на 30 дней.

Пример 3: Динамическая генерация изображения с заголовками

Пример
<?php
$width = 200;
$height = 100;
$text = 'Hello';
$im = imagecreatetruecolor($width, $height);
$bg = imagecolorallocate($im, 255, 255, 255);
$textcolor = imagecolorallocate($im, 0, 0, 0);
imagestring($im, 5, 50, 40, $text, $textcolor);
header('Content-Type: image/png');
header('Content-Disposition: inline; filename="hello.png"');
header('Cache-Control: public, max-age=86400');
imagepng($im);
imagedestroy($im);
?>

Результат:

Браузер отобразит изображение PNG с текстом, а заголовки укажут на возможность кэширования на сутки.

Пример 4: Удаление заголовков с помощью header_remove()

Пример
<?php
header('X-Powered-By: PHP/7.4');
// позже решили удалить
header_remove('X-Powered-By');
?>

Результат:

Заголовок X-Powered-By не будет включен в ответ сервера, что улучшает безопасность, скрывая версию PHP.

Пример 5: Заголовок Link для HTTP/2 Server Push

Пример
<?php
header('Link: </style.css>; rel=preload; as=style');
header('Link: </script.js>; rel=preload; as=script');
?>

Результат:

Сервер, поддерживающий HTTP/2, может отправить указанные ресурсы клиенту до запроса, сокращая время загрузки.

Установка HTTP заголовков в PHP - comments

En
Code php header (php)