Создать URL
Генератор Правила транслитерации Как сделать ЧПУ Блог Вопросы и ответы Создать URL
Архитектура

ЧПУ на самописном сайте: маршрутизация и хранение адресов

Если сайт написан своими силами, адресами управляете вы сами. Разбираем минимальную рабочую схему: от правила переписывания до хранения истории адресов.

Общая схема

Механизм состоит из четырёх частей, и каждая решает свою задачу.

  1. Правило сервера направляет все запросы в одну точку входа.
  2. Маршрутизатор разбирает путь и определяет, что запрошено.
  3. Хранилище сопоставляет текстовый адрес с записью в базе.
  4. Обработчик ошибок отвечает за ненайденные адреса и переезды.

Точка входа

Все запросы, для которых нет реального файла, отправляются в один скрипт.

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

Два условия важны: без них сервер начнёт направлять в скрипт запросы к картинкам, стилям и загруженным файлам.

Разбор пути

Маршрутизатор получает путь и раскладывает его на сегменты.

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path = trim($path, '/');
$segments = $path === '' ? [] : explode('/', $path);

Дальше логика зависит от структуры сайта. Простой вариант — сопоставление первого сегмента с типом раздела, а остального с конкретной записью.

АдресСегментыЧто показать
/—Главную
/catalog/catalogСписок разделов
/catalog/divany/catalog, divanyРаздел по slug
/blog/kak-vybrat/blog, kak-vybratСтатью по slug

Хранение адреса

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

CREATE TABLE pages (
  id INT PRIMARY KEY AUTO_INCREMENT,
  slug VARCHAR(190) NOT NULL UNIQUE,
  title VARCHAR(255) NOT NULL,
  ...
);

Причина в предсказуемости: если адрес пересчитывается динамически, любая правка заголовка молча изменит URL проиндексированной страницы. Уникальный индекс на поле защищает от совпадений на уровне базы.

Сам slug формируется один раз при сохранении — функции транслитерации для трёх языков разобраны в отдельной статье.

Длина поля 190, а не 255. При кодировке utf8mb4 и стандартном размере индекса 767 байт уникальный индекс на 255 символов не создастся. 190 символов — безопасный предел, и для адреса его более чем достаточно.

История адресов

Отдельная таблица со старыми адресами избавляет от ручной настройки редиректов при каждом переименовании.

CREATE TABLE url_history (
  old_slug VARCHAR(190) PRIMARY KEY,
  page_id INT NOT NULL,
  created_at DATETIME NOT NULL
);

Логика работы: при смене адреса старое значение записывается в историю. Маршрутизатор, не найдя страницу по текущему slug, проверяет историю и отдаёт 301 на актуальный адрес. Общие правила перенаправлений — в инструкции по 301.

Обработка ненайденных адресов

Порядок проверок при неизвестном пути:

  1. Есть ли адрес в истории переездов — отдать 301.
  2. Отличается ли путь только регистром — отдать 301 на нижний регистр.
  3. Отличается ли только слэшем на конце — отдать 301 на выбранный формат.
  4. Ничего не подошло — отдать 404 с полезной страницей.

Важно: страница ошибки должна отдавать именно код 404, а не 200 с текстом «страница не найдена». Иначе поисковая система будет считать её обычной страницей и индексировать сотнями копий.

Что учесть на старте

  • Один канонический формат адреса: регистр, слэш, наличие расширения.
  • Slug пишется в базу при сохранении, а не вычисляется при выводе.
  • Уникальность обеспечивается индексом базы, а не только кодом.
  • История адресов ведётся с первого дня — добавить её потом сложнее.
  • Внутренние ссылки строятся из хранимого slug, а не собираются вручную в шаблонах.

Производительность маршрутизации

Поиск страницы по текстовому адресу выполняется при каждом запросе, поэтому мелочи здесь имеют значение.

  • Индекс на поле slug обязателен — без него база перебирает всю таблицу.
  • Один запрос вместо цепочки. Для вложенных путей храните полный путь отдельным полем, а не собирайте его рекурсивными запросами к родителям.
  • Кеш маршрутов для статичных разделов: карта «путь → обработчик» редко меняется и хорошо кешируется.
  • Ранний выход для запросов к файлам: правило сервера уже отсекает их, но проверка в коде не помешает.

Безопасность разбора пути

Путь приходит от пользователя, а значит, это недоверенные данные. Три обязательных правила:

  1. Никогда не подставляйте сегменты пути в имена файлов напрямую — последовательности с точками позволяют выйти за пределы каталога.
  2. Экранируйте значения в запросах к базе или используйте подготовленные выражения.
  3. Ограничьте длину и набор символов ещё до поиска: адрес не может содержать ничего, кроме латиницы, цифр, дефиса и слэша.

Последнее правило заодно избавляет от лишних запросов к базе при сканировании сайта ботами.

Частые вопросы

Хранить slug в базе или вычислять из заголовка?

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

Как обеспечить уникальность адресов?

Уникальным индексом на поле slug в базе. Код при сохранении проверяет занятость и при необходимости добавляет числовой суффикс.

Зачем нужна таблица истории адресов?

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

Готовый адрес можно собрать прямо сейчас: откройте генератор ЧПУ — он транслитерирует фразу по таблице Яндекса, уберёт служебные слова и покажет длину адреса.

Читайте также