Как грамотно обрабатывать ошибку 404 в PHP приложении

Раздел: -> Обработка ошибок

Обработка ошибки 404 в PHP

Как реализовать централизованную обработку ошибки 404 с помощью единой точки входа (front controller)?

Самое эффективное решение для современных PHP-приложений - использование единой точки входа (например, index.php) вместе с серверным перенаправлением всех запросов на этот файл (через .htaccess или конфигурацию Nginx). Внутри скрипта определяется маршрут, и если он не найден, отправляется код 404 и выводится кастомная страница. Этот подход даёт полный контроль над обработкой ошибок, позволяет логировать инциденты и избегать дублирования кода.


// index.php - единая точка входа
$requestUri = $_SERVER['REQUEST_URI'];
$routes = [
    '/' => 'home',
    '/about' => 'about',
    '/contact' => 'contact'
];
if (isset($routes[$requestUri])) {
    $page = $routes[$requestUri];
    include __DIR__ . '/pages/' . $page . '.php';
} else {
    http_response_code(404);
    echo '

Страница не найдена

'; // Дополнительно: логирование error_log('404: ' . $requestUri . ' - ' . date('Y-m-d H:i:s'), 3, __DIR__ . '/errors.log'); }

Проблемы, которые могут возникнуть:

  • Если .htaccess не настроен правильно, запросы могут не перенаправляться на index.php. В таком случае сервер будет искать физический файл, и для несуществующих страниц вернёт стандартную 404 сервера, а не кастомную. Решение - проверить модуль mod_rewrite и правила в .htaccess.
  • Неверный порядок проверки маршрутов - если регулярные выражения конфликтуют, маршрут может быть не найден. Рекомендуется сначала проверять статические маршруты, потом динамические.
  • Отсутствие обработки исключений - если внутри подключаемого файла возникает ошибка, код 404 может не отправиться. Используйте try-catch.

Цель: обеспечить единообразную реакцию на отсутствующие страницы во всём приложении. Используется в проектах любой сложности, от простых сайтов до фреймворков.

Как отправить код 404 с помощью функции header() без маршрутизатора?

Можно использовать header('HTTP/1.0 404 Not Found') внутри скрипта, который вызывается напрямую. Это подходит для небольших сайтов, где каждая страница обрабатывается отдельным PHP-файлом.


// page.php
if (!isset($_GET['id']) || $_GET['id'] != 'valid') {
    header('HTTP/1.0 404 Not Found');
    echo 'Страница не найдена';
    exit;
}
Типичная ошибка: вызов header() после вывода текста. Это приводит к Warning: Cannot modify header information. Решение - помещать header() до любого вывода или использовать буферизацию (ob_start).

Цель: быстро добавить 404 на отдельные страницы. Случай: устаревшие проекты или простые скрипты без единой точки входа.

Как настроить кастомную страницу 404 через .htaccess без PHP-обработки?

Директива ErrorDocument в .htaccess позволяет задать статическую HTML-страницу или скрипт для определённых кодов ошибок.


# .htaccess
ErrorDocument 404 /errors/404.html
# или через PHP:
ErrorDocument 404 /errors/404.php
Если указанный путь неверен, сервер может показать свою стандартную страницу. Убедитесь, что файл существует и доступен для чтения. Для PHP-обработчика можно передавать параметры через переменные окружения, но это менее гибко, чем единая точка входа.

Цель: быстрое решение без написания кода, если нужна просто статическая страница. Случай: сайты на чистом HTML с небольшим количеством PHP-вставок.

Как обработать 404 при использовании try-catch и исключений?

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


class NotFoundException extends Exception {}
try {
    $id = $_GET['id'] ?? 0;
    if ($id === 0) {
        throw new NotFoundException('Ресурс не найден');
    }
    // ... остальная логика
} catch (NotFoundException $e) {
    http_response_code(404);
    echo '404: ' . $e->getMessage();
    error_log($e->getMessage());
}
Исключения могут быть не перехвачены, если не использовать глобальный обработчик (set_exception_handler). Также неправильная иерархия исключений приведёт к тому, что 404 будет обработана как 500.

Цель: структурированная обработка ошибок в ООП-проектах. Случай: приложения с чёткой архитектурой, где ошибки - часть бизнес-логики.

Как использовать http_response_code() для отправки 404?

Современная альтернатива header() - функция http_response_code(), доступная с PHP 5.4.


http_response_code(404);
echo 'Страница не найдена';
Эта функция не останавливает выполнение скрипта - после неё можно продолжать выводить контент. Для полной остановки используйте exit или die. Но если не остановить, браузер может получить смешанный контент.

Цель: простой способ изменить HTTP-статус. Случай: API-ответы, где надо вернуть JSON с кодом 404.

Расширенные примеры обработки ошибки 404 в PHP с пояснениями и результатами.

Пример 1: Простой маршрутизатор с регулярными выражениями

Пример

// index.php
$request = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$pattern = '~^/user/(\d+)$~';
if (preg_match($pattern, $request, $matches)) {
    $userId = (int)$matches[1];
    echo "Профиль пользователя ID: $userId";
} else {
    http_response_code(404);
    echo '

404 - Такой страницы нет

'; echo '

Проверьте URL или вернитесь на главную.

'; }
Запрос: /user/42 → "Профиль пользователя ID: 42"
Запрос: /unknown → "404 - Такой страницы нет" и статус 404

Пояснение: маршрутизация через регулярное выражение позволяет обрабатывать динамические пути. При отсутствии совпадения отправляется 404.

Пример 2: Кастомная страница 404 с использованием include

Пример

// index.php
$page = __DIR__ . '/pages' . $_SERVER['REQUEST_URI'] . '.php';
if (file_exists($page)) {
    include $page;
} else {
    http_response_code(404);
    include __DIR__ . '/errors/404.php';
}
При обращении к /about (если /pages/about.php существует) - подключается about.php
При обращении к /nonexistent - подключается errors/404.php, статус 404

Пояснение: простое сопоставление URI с файловой структурой. Недостаток - уязвимость к Path Traversal, если не фильтровать входные данные.

Пример 3: Логирование 404 в базу данных

Пример

// db_log.php
$pdo = new PDO('mysql:host=localhost;dbname=logs', 'user', 'pass');
$stmt = $pdo->prepare('INSERT INTO error_404 (url, ip, time) VALUES (?, ?, ?)');
$stmt->execute([$_SERVER['REQUEST_URI'], $_SERVER['REMOTE_ADDR'], date('Y-m-d H:i:s')]);
http_response_code(404);
echo 'Страница не найдена. Мы записали этот инцидент.';
Запись в БД: url='/broken', ip='192.168.1.1', time='2025-04-08 12:00:00'
Пользователь видит сообщение и статус 404.

Пояснение: полезно для анализа популярных битых ссылок. Требует подключения к БД и правильной обработки исключений при записи.

Пример 4: Обработка 404 в REST API с JSON-ответом

Пример

// api.php
header('Content-Type: application/json');
$resource = $_GET['resource'] ?? '';
$validResources = ['users', 'posts'];
if (!in_array($resource, $validResources)) {
    http_response_code(404);
    echo json_encode([
        'error' => 'Resource not found',
        'code' => 404,
        'message' => 'Запрашиваемый ресурс не существует.'
    ]);
    exit;
}
// … обработка ресурса …
Запрос: /api.php?resource=invalid → HTTP/1.1 404 Not Found, тело:
{"error":"Resource not found","code":404,"message":"Запрашиваемый ресурс не существует."}

Пояснение: для API важно возвращать правильный код и структурированное тело ответа. Статус 404 гарантирует, что клиент (например, fetch) распознает ошибку.

Пример 5: Глобальный обработчик 404 через set_exception_handler

Пример

// main.php
function customExceptionHandler($exception) {
    if ($exception instanceof \NotFoundException) {
        http_response_code(404);
        echo '

404 - ' . $exception->getMessage() . '

'; } else { http_response_code(500); echo 'Внутренняя ошибка сервера'; } } set_exception_handler('customExceptionHandler'); // Пример throw new \NotFoundException('Страница не найдена');
Выбрасывается NotFoundException → страница выводит статус 404 и сообщение "Страница не найдена"

Пояснение: глобальный обработчик упрощает код - не нужно везде писать try-catch. Подходит для фреймворков и приложений, где все исключения наследуются от базового класса.

Ошибка 404 в PHP - comments

En
Php not found (php)