Обработка ошибок HTTP 404 в PHP приложениях

Раздел: Администрирование и отладка -> Обработка ошибок

Когда пользователь переходит по несуществующему адресу, сервер должен вернуть HTTP статус 404 и показать информативную страницу. В PHP есть несколько подходов для реализации этой задачи. Рассмотрим наиболее эффективные и популярные варианты.

Основные методы обработки 404 ошибки

Единая точка входа с маршрутизатором

Этот подход считается наиболее гибким и мощным, особенно для приложений, построенных по паттерну MVC. Все запросы направляются через один файл (index.php), а маршрутизатор определяет, какой контроллер и действие вызвать. Если маршрут не найден, устанавливается код 404 и выводится кастомная страница.

Как организовать единую точку входа и маршрутизацию для обработки 404?

  1. Создать файл index.php в корне проекта.
  2. Настроить веб-сервер (Apache с mod_rewrite) с помощью .htaccess.
  3. Реализовать класс Router с методом dispatch.
  4. В случае отсутствия маршрута установить статус 404 и вывести страницу.

Пример .htaccess:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Php not found (ошибка 404 в php)

Пример index.php:

<?php
require_once 'Router.php';
$router = new Router();
// Регистрация маршрутов
$router->addRoute('/', 'HomeController@index');
$router->addRoute('/about', 'AboutController@index');
// Диспетчеризация
$router->dispatch($_SERVER['REQUEST_URI']);
?>

Php try (обработка исключений try-catch в php)

Пример Router.php (фрагмент):

<?php
class Router {
    private $routes = [];

    public function addRoute($uri, $handler) {
        $this->routes[trim($uri, '/')] = $handler;
    }

    public function dispatch($uri) {
        $uri = trim(parse_url($uri, PHP_URL_PATH), '/');
        if (array_key_exists($uri, $this->routes)) {
            // вызов контроллера
            list($controller, $action) = explode('@', $this->routes[$uri]);
            $ctrl = new $controller();
            $ctrl->$action();
        } else {
            http_response_code(404);
            include '404.php';
            exit;
        }
    }
}
?>

Php код ответа (код ответа http в php)

Возможные проблемы и их решения:

  • mod_rewrite не включен – в Apache необходимо включить модуль (a2enmod rewrite) и разрешить переопределение (AllowOverride All).
  • Пути не совпадают – в .htaccess путь к index.php должен быть корректным. Для подкаталогов используется RewriteBase /subdir/.
  • После header() не вызван exit – скрипт продолжает выполнение, возможен вывод лишнего контента. Всегда завершайте выполнение после установки 404.

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

Как вручную отправить статус 404 и показать простое сообщение?

Самый простой способ – использовать функцию header() с соответствующим кодом и сразу вывести HTML.

<?php
header("HTTP/1.0 404 Not Found");
echo "<h1>Страница не найдена</h1><p>Проверьте адрес.</p>";
exit;
?>

Post 500 php (ошибка 500 при post-запросе в php)

Важно: header() должен вызываться до любого вывода, иначе PHP выдаст предупреждение. Если вывод уже начался, используйте буферизацию (ob_start()).

Типичная ошибка: ‘Cannot modify header information – headers already sent’. Решение: проверьте, что перед header() нет пробелов или echo. Либо используйте буферизацию: ob_start() в начале скрипта.

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

Как настроить Apache на автоматический показ 404 страницы через .htaccess?

С помощью директивы ErrorDocument можно указать файл, который будет отдаваться при ошибке 404.

ErrorDocument 404 /error/404.php

Php страница не найдена (страница 404 в php)

В файле 404.php:

<?php
http_response_code(404);
?>
<!DOCTYPE html>
<html><head><title>404</title></head>
<body><h1>Страница не найдена</h1></body>
</html>

Access denied php (ошибка доступа в php)

Проблема: если сам файл ошибки содержит ошибки PHP, сервер вернет 500. Убедитесь, что файл существует и синтаксически корректен. Также настройка работает только на Apache с включенным mod_rewrite и AllowOverride.

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

Как обрабатывать 404 в собственном MVC фреймворке?

В роутере при отсутствии совпадения следует вызывать специальный контроллер ошибок. Это позволяет использовать один шаблон для всех ошибок.

// В методе dispatch роутера
if (!array_key_exists($uri, $this->routes)) {
    http_response_code(404);
    $controller = new ErrorController();
    $controller->notFound();
    exit;
}

Pdo php try (pdo php try)

В ErrorController:

<?php
class ErrorController {
    public function notFound() {
        include 'views/404.php';
    }
}
?>

Php include try (php include с try)

Этот подход легко расширяется: можно добавить логирование, отображение разных шаблонов для разных окружений (dev/prod).

Частая ошибка – забыть установить http_response_code(404) перед выводом. Без этого браузер получит статус 200, что некорректно.

Цели: для приложений, использующих MVC архитектуру, где требуется единый стиль ошибок и возможность расширения.

Как вернуть JSON с ошибкой 404 для REST API?

Если приложение отдает данные в формате JSON, ответ на 404 должен быть структурированным.

// В роутере или контроллере
http_response_code(404);
header('Content-Type: application/json');
echo json_encode([
    'error' => 'Not Found',
    'message' => 'Запрашиваемый ресурс не существует',
    'code' => 404
]);
exit;

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

Проблема: если не установлен заголовок Content-Type, клиент может не распознать JSON. Всегда указывайте правильный MIME-тип.

Цели: для API, микросервисов, одностраничных приложений, где ожидается JSON ответ.

Общие проблемы при обработке 404 в PHP:

  • Кэширование 404 страницы браузером. Используйте заголовки Cache-Control: no-cache для динамических страниц.
  • Неправильные настройки сервера (например, в nginx нужно отдельно настраивать try_files).
  • Отсутствие логирования. Для отладки полезно записывать запрошенные URI в лог.
- Warning php page (предупреждение php)
- Reporting php (настройка отображения ошибок php)
- Php error log (лог ошибок php)

Расширенные примеры реализации 404

Полноценный роутер с регулярными выражениями и Middleware для 404

Ниже представлен класс Router, который поддерживает динамические параметры в URI, а также вызывает middleware перед обработкой ошибки 404 (например, для логирования).

Пример
<?php
class Router {
    private $routes = [];
    private $middleware = [];

    public function addRoute($pattern, $handler) {
        // Преобразуем {param} в именованные группы
        $pattern = preg_replace('/\{([a-zA-Z_]+)\}/', '(?P<$1>[^/]+)', $pattern);
        $this->routes['#^' . $pattern . '$#'] = $handler;
    }

    public function addMiddleware($closure) {
        $this->middleware[] = $closure;
    }

    public function dispatch($uri) {
        $uri = trim(parse_url($uri, PHP_URL_PATH), '/');
        foreach ($this->routes as $pattern => $handler) {
            if (preg_match($pattern, $uri, $matches)) {
                // Вызов middleware
                foreach ($this->middleware as $mw) {
                    $mw($uri);
                }
                // Вызов контроллера с параметрами
                list($controller, $action) = explode('@', $handler);
                $ctrl = new $controller();
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                $ctrl->$action($params);
                return;
            }
        }
        // Middleware для 404
        foreach ($this->middleware as $mw) {
            $mw($uri, true);
        }
        http_response_code(404);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['error' => 'Not Found', 'uri' => $uri]);
        exit;
    }
}

// Пример использования
$router = new Router();
$router->addMiddleware(function($uri, $is404 = false) {
    if ($is404) {
        error_log("404: $uri at " . date('Y-m-d H:i:s'));
    }
});
$router->addRoute('/', 'HomeController@index');
$router->addRoute('/user/{id}', 'UserController@show');
$router->dispatch($_SERVER['REQUEST_URI']);
?>
Результат: при запросе /user/42 вызывает UserController::show(['id'=>'42']); при запросе /nonexistent в лог пишется '404: nonexistent' и возвращается JSON.

Настройка 404 в nginx с передачей на PHP-обработчик

Для nginx конфигурация отличается от Apache. Пример конфигурации сервера:

Пример
server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    error_page 404 /404.php;
    location = /404.php {
        internal;
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/404.php;
    }
}

В файле 404.php устанавливается статус и выводится страница:

Пример
<?php
http_response_code(404);
?>
<html>...</html>

В nginx блок error_page должен быть до обработки PHP, иначе 404 может отдаваться как статика. Ошибка: 'primary script unknown' – проверьте путь к SCRIPT_FILENAME.

Обработка 404 с учётом Accept-Header (HTML vs JSON)

Один обработчик может определять ожидаемый формат ответа:

Пример
<?php
function handle404() {
    http_response_code(404);
    $accept = $_SERVER['HTTP_ACCEPT'] ?? '';
    if (strpos($accept, 'application/json') !== false) {
        header('Content-Type: application/json');
        echo json_encode(['status' => 404, 'message' => 'Resource not found']);
    } else {
        header('Content-Type: text/html; charset=utf-8');
        include '404.html';
    }
    exit;
}
?>
Результат: браузер получает HTML, а API-клиент - JSON.

Логирование 404 с деталями запроса

Помимо записи в error_log, можно сохранять в базу данных информацию о несуществующих запросах:

Пример
<?php
function log404($uri, $method, $ip) {
    $pdo = new PDO('sqlite:404.log.db');
    $pdo->exec('CREATE TABLE IF NOT EXISTS errors (id INTEGER PRIMARY KEY, uri TEXT, method TEXT, ip TEXT, time DATETIME DEFAULT CURRENT_TIMESTAMP)');
    $stmt = $pdo->prepare('INSERT INTO errors (uri, method, ip) VALUES (?, ?, ?)');
    $stmt->execute([$uri, $method, $ip]);
}

// В обработчике 404
log404($_SERVER['REQUEST_URI'], $_SERVER['REQUEST_METHOD'], $_SERVER['REMOTE_ADDR']);
?>
Результат: в SQLite-файл записывается URI, метод и IP с временной меткой.

Страница 404 в PHP - comments

En
Php страница не найдена (php)