HTTP коды ответа в PHP: как правильно управлять статусами
Обработка HTTP кодов ответа в PHP
Корректная установка кодов состояния HTTP играет важную роль для SEO, работы API, обработки ошибок и пользовательского опыта. В PHP существует несколько способов задать код ответа, каждый из которых имеет свои особенности и области применения.
Основное решение: функция http_response_code()
Начиная с PHP 5.4, рекомендуется использовать встроенную функцию http_response_code(). Она позволяет как установить, так и получить текущий код ответа.
Как задать код 404 с помощью http_response_code?
<?php
http_response_code(404);
?>В этом примере функция устанавливает код 404 (Not Found). При этом заголовок отправляется автоматически, если вывод ещё не начался. Функция также может быть вызвана без аргументов для получения текущего кода.
Типичные проблемы:
- Если до вызова http_response_code() уже был отправлен какой-либо вывод (echo, пробелы), заголовок не установится. Решение - убедиться, что скрипт не производит вывод до вызова, либо использовать буферизацию (ob_start()).
- В некоторых окружениях (например, FastCGI) http_response_code() может не сработать, если ранее был отправлен нестандартный заголовок. В таких случаях помогает комбинация с header().
Вариант 1: Установка кода через header()
Как задать код 403 с помощью header?
<?php
header('HTTP/1.1 403 Forbidden');
?>Этот способ работает во всех версиях PHP и даёт полный контроль над строкой статуса. Рекомендуется явно указывать протокол (HTTP/1.1).
Проблема: если используется http_response_code() после header(), код может перезаписаться. Лучше выбрать один метод.
Вариант 2: Получение текущего кода ответа
Как узнать, какой статус был установлен?
<?php
http_response_code(404);
$code = http_response_code();
echo $code; // 404
?>Для старых версий PHP (до 5.4) можно использовать функцию headers_list():
<?php
header('HTTP/1.1 404 Not Found');
$headers = headers_list();
echo $headers[0]; // HTTP/1.1 404 Not Found
?>Функция headers_list() возвращает все отправленные заголовки; для извлечения кода требуется парсинг строки.
Вариант 3: Редирект с кодом 301 или 302
Как сделать перенаправление с правильным статусом?
<?php
header('Location: /new-page', true, 301);
exit;
?>Третий параметр header() задаёт код ответа. Для постоянного редиректа используется 301, для временного - 302. Обязателен вызов exit, чтобы скрипт не продолжал выполнение.
Если после header() не вызвать exit, может быть отправлен дополнительный контент, что приведёт к ошибке или нежелательному поведению.
Вариант 4: Отправка JSON с кодом для API
Как вернуть статус 422 и сообщение в формате JSON?
<?php
http_response_code(422);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['error' => 'Invalid input'], JSON_UNESCAPED_UNICODE);
exit;
?>Этот подход часто используется в REST API. Код состояния устанавливается до вывода JSON.
Ошибка: если не указать заголовок Content-Type, клиент может не распознать ответ как JSON. Также следует избегать вывода до установки кода.
Вариант 5: Пользовательская страница ошибки 404
Как отобразить кастомную страницу при отсутствии ресурса?
<?php
if (!$pageFound) {
http_response_code(404);
include '404.php';
exit;
}
?>Подключается отдельный файл с HTML-разметкой ошибки. Важно, чтобы в файле не было лишнего вывода до установки кода.
Браузеры могут кэшировать страницу 404 - добавляйте заголовки Cache-Control: no-cache для статических ошибок.
Вариант 6: Обработка исключений с кодом 500
Как отправить статус 500 при возникновении исключения?
<?php
try {
// опасный код
} catch (Exception $e) {
http_response_code(500);
echo 'Внутренняя ошибка сервера';
// логирование $e->getMessage()
}
?>В production режиме не рекомендуется выводить текст исключения.
Если исключение перехватывается в нескольких местах, код может быть установлен повторно. Следите за порядком вызовов.
Вариант 7: Установка кода без http_response_code (PHP < 5.4)
Как задать статус в старых версиях PHP?
<?php
header('HTTP/1.1 403 Forbidden');
?>Если требуется поддержка очень старых систем, можно использовать функцию header() с полной строкой статуса. Однако современное окружение обычно включает PHP 5.4+.
Протокол (HTTP/1.0 или HTTP/1.1) должен соответствовать возможностям сервера. В большинстве случаев подходит HTTP/1.1.
Расширенные примеры работы с HTTP кодами
Ниже приведены развёрнутые примеры с пояснениями, демонстрирующие различные сценарии применения HTTP кодов в PHP.
1. Отправка разных кодов в зависимости от логики
Пример обработки REST запроса с возвратом кодов 200, 201, 204, 400, 401, 404, 409, 422, 500.
<?php
// Симуляция запроса
$method = $_SERVER['REQUEST_METHOD'];
$resource = $_GET['resource'] ?? null;
if ($method === 'POST' && $resource === 'users') {
// Успешное создание
http_response_code(201);
header('Content-Type: application/json');
echo json_encode(['id' => 42, 'message' => 'Created']);
} elseif ($method === 'GET' && $resource === 'users') {
// Успешный запрос
http_response_code(200);
echo json_encode(['users' => []]);
} elseif ($method === 'DELETE' && $resource === 'users') {
// Нет содержимого
http_response_code(204);
exit;
} elseif ($method === 'GET' && !$resource) {
// Неверный запрос
http_response_code(400);
echo json_encode(['error' => 'Resource parameter missing']);
} else {
// Не найден
http_response_code(404);
echo json_encode(['error' => 'Not found']);
}
?>Результат: заголовки ответа с соответствующими кодами и JSON телом.
2. Обработка авторизации с кодом 401
<?php
session_start();
if (!isset($_SESSION['user'])) {
http_response_code(401);
header('WWW-Authenticate: Bearer realm="API"');
echo json_encode(['error' => 'Unauthorized']);
exit;
}
// остальной код
?>Клиент получит 401 и заголовок WWW-Authenticate, указывающий на необходимость авторизации.
3. Буферизация вывода для установки кода после частичного вывода
Иногда требуется установить код после того, как часть данных уже была отправлена. В таких случаях помогает ob_start().
<?php
ob_start();
echo 'Неполный вывод';
if (some_error()) {
ob_clean(); // очищаем буфер
http_response_code(500);
echo 'Ошибка';
}
ob_end_flush();
?>Если ошибки нет, будет выведен "Неполный вывод". При ошибке буфер очищается и отправляется код 500.
4. Логирование кодов ответа с деталями
<?php
function respondAndLog($code, $message, $data = []) {
http_response_code($code);
header('Content-Type: application/json');
$response = ['code' => $code, 'message' => $message];
if ($data) {
$response['data'] = $data;
}
echo json_encode($response, JSON_UNESCAPED_UNICODE);
file_put_contents('/var/log/http_codes.log', date('Y-m-d H:i:s') . " $code $message\n", FILE_APPEND);
exit;
}
// Использование
respondAndLog(422, 'Validation failed', ['errors' => ['email' => 'Invalid format']]);
?>Файл лога получит строку с временем, кодом и сообщением.
5. Комбинирование с try-catch и кастомным обработчиком ошибок
<?php
set_exception_handler(function ($exception) {
http_response_code(500);
header('Content-Type: application/json');
echo json_encode(['error' => 'Internal Server Error']);
file_put_contents('/var/log/exceptions.log', $exception->getMessage() . "\n", FILE_APPEND);
exit;
});
// Здесь может быть код, выбрасывающий исключение
throw new Exception('Что-то пошло не так');
?>Все необработанные исключения приведут к ответу 500 с JSON.
6. Использование header_remove() для сброса кода
<?php
http_response_code(200);
header('X-Custom: value');
// Позже решаем изменить код
header_remove('X-Custom');
http_response_code(403);
?>Удаляется ранее установленный заголовок X-Custom, код заменяется на 403.
7. Работа с FastCGI: альтернативный способ
<?php
if (function_exists('fastcgi_finish_request')) {
http_response_code(200);
echo 'OK';
fastcgi_finish_request();
// длительная операция после отправки ответа
}
?>После fastcgi_finish_request() можно выполнять длительные задачи, не блокируя клиента.
Все примеры требуют, чтобы до установки кода не было вывода (кроме случаев с буферизацией). Соблюдение этого правила гарантирует корректную работу HTTP кодов в PHP.