Отправка 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)
Пояснение шагов:
http_response_code(200)- явно указывает HTTP статус. Если опустить, PHP автоматически установит 200 при первом выводе.header('Content-Type: ...')- задаёт тип контента. Должен быть вызван до любого вывода (кроме случая разрешённой буферизации).- Вывод тела ответа через
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).