Отправка HTTP ответа в PHP: коды состояния и заголовки

Раздел: Веб-разработка -> Коды ответа

HTTP ответ в PHP: управление кодами состояния и заголовками

Основной способ: функции http_response_code() и header()

Наиболее универсальное решение для задания кода ответа и заголовков в PHP - комбинация http_response_code() (или http_response_code без аргумента для чтения текущего кода) и header(). Этот подход позволяет точно контролировать статус и метаданные ответа.

<?php
// Устанавливаем код 200 OK
http_response_code(200);
header('Content-Type: text/html; charset=utf-8');
echo '<h1>Успешный ответ</h1>';
?>

Php http ответ (http ответ в php)

Пояснение шагов:

  1. http_response_code(200) - явно указывает HTTP статус. Если опустить, PHP автоматически установит 200 при первом выводе.
  2. header('Content-Type: ...') - задаёт тип контента. Должен быть вызван до любого вывода (кроме случая разрешённой буферизации).
  3. Вывод тела ответа через echo.

Проблемы и ошибки:

  • Если перед header() уже был вывод (включая пробелы до <?php), PHP выдаст ошибку «Headers already sent». Решение - использовать буферизацию (ob_start()) или проверять, что нет лишнего вывода.
  • Вызов http_response_code() после вывода не имеет эффекта, код уже отправлен.

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

Можно напрямую задать статусную строку через header():

<?php
header('HTTP/1.1 404 Not Found');
echo 'Страница не найдена';
?>

Этот способ работает для любых кодов, но требует знания текстового описания статуса. Если описание неверное, клиент может интерпретировать ответ некорректно.

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

При использовании устаревшей версии HTTP (например, HTTP/1.0) строка должна соответствовать протоколу. PHP сам подставляет протокол из $_SERVER['SERVER_PROTOCOL'], если не указан явно.

Как вернуть API ответ с кодом и данными в формате JSON?

Типичная задача для REST API: отправить JSON с нужным статусом.

<?php
$data = ['status' => 'ok', 'message' => 'Запрос выполнен'];
http_response_code(200);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE);
?>

Цель:

Обеспечить правильную сериализацию и установку кода. JSON_UNESCAPED_UNICODE сохраняет кириллицу.

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

Забыть установить заголовок Content-Type, тогда браузер может интерпретировать JSON как текст или HTML. Также не следует оборачивать JSON в HTML.

Как сделать редирект с кодом 301 или 302?

Перенаправление выполняется через заголовок Location с обязательным завершением работы скрипта (exit или die), чтобы избежать дальнейшего вывода.

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

Случай использования:

301 - постоянный редирект (SEO), 302 - временный. После header('Location: ...') всегда вызывайте exit, иначе скрипт может продолжить выполнение и отправить ненужные данные.

Ошибка:

Если перед заголовком был вывод (например, echo 'текст';), редирект не сработает. Решение - буферизация или проверка.

Как добавить дополнительные заголовки, например для CORS или кэширования?

Заголовки CORS, Cache-Control, X-Content-Type-Options добавляются через header().

<?php
http_response_code(200);
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Cache-Control: max-age=3600');
header('X-Content-Type-Options: nosniff');
echo 'Ответ с дополнительными заголовками';
?>

Это необходимо при разработке API, доступных с других доменов, или для управления кэшированием.

Частая ошибка:

Неправильная установка CORS-заголовков может привести к блокировке запросов браузером. Проверьте, что сервер отправляет правильные значения для Access-Control-Allow-Origin.

Как отправить файл для скачивания с правильными заголовками?

Используется заголовок Content-Disposition для принудительного скачивания.

<?php
$file = 'path/to/document.pdf';
if (file_exists($file)) {
    http_response_code(200);
    header('Content-Description: File Transfer');
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="document.pdf"');
    header('Content-Length: ' . filesize($file));
    readfile($file);
    exit;
} else {
    http_response_code(404);
    echo 'Файл не найден';
}
?>

Цель:

Передать файл, указав его размер, тип и имя для сохранения. readfile() выводит содержимое порциями, экономя память.

Проблема:

Без указания Content-Length браузер не сможет отобразить прогресс загрузки. Также возможна утечка памяти при чтении больших файлов без readfile или fpassthru.

Расширенные и нестандартные примеры управления HTTP ответом в PHP

Отправка ответа с кодом 201 Created с указанием Location

Пример
<?php
// Создание нового ресурса (например, пользователя)
$newId = 42;
http_response_code(201);
header('Location: /users/' . $newId);
header('Content-Type: application/json');
echo json_encode(['id' => $newId, 'status' => 'created']);
?>
HTTP/1.1 201 Created
Location: /users/42
Content-Type: application/json

{"id":42,"status":"created"}

Пояснение: код 201 означает успешное создание, заголовок Location указывает URL нового ресурса. Важно отправить все заголовки до вывода тела.

Ответ с кодом 403 Forbidden и кастомным сообщением

Пример
<?php
http_response_code(403);
header('Content-Type: text/plain');
echo 'Доступ запрещён: недостаточно прав.';
?>
HTTP/1.1 403 Forbidden
Content-Type: text/plain

Доступ запрещён: недостаточно прав.

Используется для ограничения доступа к защищённым ресурсам. Рекомендуется не раскрывать детали.

Ответ с кодом 503 Service Unavailable и заголовком Retry-After

Пример
<?php
http_response_code(503);
header('Retry-After: 3600'); // повторить через час
header('Content-Type: text/html');
echo '<h1>Сервис временно недоступен</h1><p>Попробуйте позже.</p>';
?>
HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: text/html

<h1>Сервис временно недоступен</h1><p>Попробуйте позже.</p>

Обработка ошибок с установкой кода 500 и логированием

Пример
<?php
ob_start();
try {
    // некий код, который может выбросить исключение
    if (rand(0,1)) throw new Exception('Неизвестная ошибка');
    http_response_code(200);
    echo 'Успех';
} catch (Exception $e) {
    ob_clean(); // очистить ранее накопленный вывод
    http_response_code(500);
    header('Content-Type: text/plain');
    echo 'Внутренняя ошибка сервера';
    error_log($e->getMessage());
} finally {
    ob_end_flush();
}
?>
(в случае исключения) HTTP/1.1 500 Internal Server Error

Буферизация позволяет избежать «headers already sent» при исключении. Логирование ошибки не показывается клиенту.

Отправка разного ответа в зависимости от HTTP метода (REST)

Пример
<?php
$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
    case 'GET':
        http_response_code(200);
        echo json_encode(['data' => 'список']);
        break;
    case 'POST':
        http_response_code(201);
        echo json_encode(['status' => 'создано']);
        break;
    case 'PUT':
        http_response_code(200);
        echo json_encode(['status' => 'обновлено']);
        break;
    case 'DELETE':
        http_response_code(204); // No Content
        // без тела
        break;
    default:
        http_response_code(405);
        header('Allow: GET, POST, PUT, DELETE');
        echo json_encode(['error' => 'Метод не поддерживается']);
}
?>
(пример для DELETE) HTTP/1.1 204 No Content

Код 204 используется для успешного удаления, тело ответа отсутствует.

Установка заголовков для предотвращения кэширования (динамический контент)

Пример
<?php
http_response_code(200);
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
echo date('Y-m-d H:i:s');
?>
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

2025-03-28 12:34:56

Эти заголовки заставляют браузер и прокси не кэшировать ответ, подходит для страниц с часто меняющимся содержимым.

Отправка gzip-сжатого ответа вручную

Пример
<?php
$data = str_repeat('Тестовая строка для сжатия ', 1000);
$compressed = gzencode($data, 9);
header('Content-Encoding: gzip');
header('Content-Length: ' . strlen($compressed));
header('Content-Type: text/plain; charset=utf-8');
echo $compressed;
?>
(результат - сжатый двоичный поток)

Обычно сжатие включается на уровне сервера (mod_deflate), но для специфичных случаев можно сжать данные программно. Перед сжатием убедитесь, что клиент принимает gzip (заголовок Accept-Encoding).

HTTP ответ в PHP - comments

En
Php http ответ (php)