Маршрутизация и русские символы в URL: работа с index.php

Раздел: Веб-программирование на PHP -> Маршрутизация

Маршрутизация URL с русскими символами через index.php

Наиболее эффективный способ обработки русскоязычных URL в одностраничном приложении (Single Entry Point) — использование модуля mod_rewrite в паре с аккуратным декодированием кириллицы. Этот подход позволяет перенаправлять все запросы на index.php и при этом корректно передавать русские буквы, закодированные в URL как UTF-8.


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

Сервер Apache передаёт переменную $_GET['path'] в сыром виде (процентная кодировка). В PHP необходимо применить rawurldecode() для восстановления русского текста.


$rawPath = $_GET['path'] ?? '';
$path = rawurldecode($rawPath);
// Пример: /категория/статья → 'категория/статья'

Типичные ошибки:

  • Использование urldecode() вместо rawurldecode() может привести к потере символа '+', который интерпретируется как пробел.
  • Двойное кодирование: если в .htaccess не указан флаг B (без преобразования), Apache может повторно закодировать кириллицу.
  • Игнорирование настройки AcceptPathInfo — в некоторых конфигурациях PATH_INFO теряется.

Какая конфигурация сервера обеспечивает передачу русских букв в URL без искажений?

Основной случай использования — любой сайт с русскоязычным контентом, где маршруты содержат кириллицу (например, /блог/статья-о-пхп). Метод гарантирует корректную работу с любыми серверами Apache, поддерживающими UTF-8.

Как обработать русский URL через PATH_INFO без mod_rewrite?

Если mod_rewrite недоступен, можно использовать переменную $_SERVER['PATH_INFO']. Она появляется, когда запрос приходит напрямую к index.php/path (слэш после имени файла). Сервер передаёт часть пути после имени скрипта в этой переменной.


// URL: http://example.com/index.php/русский-путь
$path = isset($_SERVER['PATH_INFO']) ? trim($_SERVER['PATH_INFO'], '/') : '';
$path = rawurldecode($path);

Проблема: PATH_INFO может отсутствовать в некоторых конфигурациях (например, Nginx или при включённом CGIPathInfo в php-fpm). Также теряются query-параметры после пути.

Как применить разбор REQUEST_URI вручную для русских URL?

Можно вручную извлечь путь из $_SERVER['REQUEST_URI'] и удалить имя скрипта. Этот метод не зависит от mod_rewrite.


$requestUri = $_SERVER['REQUEST_URI'];
$scriptName = $_SERVER['SCRIPT_NAME'];
$path = str_replace($scriptName, '', $requestUri);
$path = parse_url($path, PHP_URL_PATH);
$path = rawurldecode(trim($path, '/'));

Ошибки: если SCRIPT_NAME не совпадает с путём из-за символических ссылок или редиректов, разбор будет неверен. Также не обрабатывается ?query — его нужно удалять через parse_url.

Как реализовать маршрутизацию с русским URL через библиотеку FastRoute?

Библиотека nikic/fast-route поддерживает UTF-8 в шаблонах, если задать корректную обработку URI. Пример настройки:


$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    $r->addRoute('GET', '/блог/{slug:.*}', 'blog_handler');
});

$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = rawurldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);

Библиотека по умолчанию не производит декодирование, поэтому декодировать URI нужно до передачи в диспетчер. Регулярные выражения со специальными символами (например, . или +) в русском тексте могут нарушить шаблон.

Расширенные примеры обработки русских URL в index.php

Пример 1: mod_rewrite + rawurldecode с фильтром безопасности

Пример

# .htaccess
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [B,L,QSA]

# index.php
$url = $_GET['url'] ?? '';
$url = rawurldecode($url);
// Удаление небезопасных символов, кроме кириллицы, слешей и дефисов
$url = preg_replace('/[^\p{Cyrillic}\w\/-]/u', '', $url);
$segments = explode('/', $url);
print_r($segments);
Для URL /привет-мир/добро-пожаловать
Вывод:
Array
(
    [0] => привет-мир
    [1] => добро-пожаловать
)

Результат:

Массив сегментов пути с корректно восстановленной кириллицей.

Пример 2: Обработка через PATH_INFO с fallback на REQUEST_URI

Пример

function getRussianPath() {
    if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] !== '') {
        $path = $_SERVER['PATH_INFO'];
    } else {
        // Fallback: извлечение из REQUEST_URI
        $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        $scriptName = dirname($_SERVER['SCRIPT_NAME']);
        if ($scriptName === '/') $scriptName = '';
        $path = substr($path, strlen($scriptName));
    }
    return rawurldecode(trim($path, '/'));
}

$path = getRussianPath();
echo "Путь: $path";
URL: http://example.com/index.php/русский/текст
Путь: русский/текст

Пример 3: Использование parse_str с кириллическими параметрами в GET

Пример

// URL: index.php?page=новости&id=5
$page = $_GET['page'] ?? '';
$page = mb_convert_encoding($page, 'UTF-8', 'auto');
echo "Страница: $page<br>";
echo "ID: " . (int)($_GET['id'] ?? 0);
Страница: новости
ID: 5

Если сервер не настроен на UTF-8, $_GET может содержать искажённые данные. Применение mb_convert_encoding с автоопределением исправляет ситуацию, но добавляет нагрузку. Лучше установить default_charset в UTF-8 в php.ini.

Пример 4: Ручная сборка маршрутизатора с поддержкой Unicode

Пример

class Router {
    private array $routes = [];

    public function add(string $pattern, callable $handler): void {
        // Экранируем слеши, заменяем {param} на захватывающую группу
        $pattern = preg_replace('/\//', '\/', $pattern);
        $pattern = preg_replace('/\{([a-z]+)\}/', '(?P<$1>[^/]+)', $pattern);
        $this->routes[] = [
            'pattern' => '/^' . $pattern . '$/u',
            'handler' => $handler
        ];
    }

    public function dispatch(string $uri): void {
        $uri = rawurldecode($uri);
        foreach ($this->routes as $route) {
            if (preg_match($route['pattern'], $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                call_user_func($route['handler'], $params);
                return;
            }
        }
        // 404
    }
}

$router = new Router();
$router->add('блог/{slug}', function($params) {
    echo "Запрос блога со слагом: " . $params['slug'];
});

$router->dispatch($_SERVER['REQUEST_URI']);
URL: /блог/заметка-про-php
Запрос блога со слагом: заметка-про-php

Пример 5: Обработка кириллических GET-параметров через mod_rewrite (без PATH_INFO)

Пример

# .htaccess
RewriteRule ^(.*)$ index.php [E=RAW_PATH:$1,END]

# index.php
$path = $_SERVER['REDIRECT_RAW_PATH'] ?? $_SERVER['RAW_PATH'] ?? '';
$path = rawurldecode($path);
// Дальнейшая обработка

Переменная окружения может называться по-разному в зависимости от версии Apache. Иногда требуется REDIRECT_RAW_PATH.

URL страницы index.php на русском - comments

En
Index php page url ru (php)