Новинка Виртуальный VDS сервер в Финляндии от 390р
Выберите продукт

Apache без .htaccess: перенос правил в vhost и ускорение сайта

.htaccess удобен, но стоит производительности и прозрачности. Покажу, как перенести логику в конфигурацию виртуального хоста: переписывания, доступ, заголовки и кеш, выключить AllowOverride и получить прирост скорости. Разберём план миграции, тестирование, трассировку и безопасный откат.
Apache без .htaccess: перенос правил в vhost и ускорение сайта

Зачем избавляться от .htaccess

.htaccess исторически решает задачи делегирования прав: веб‑приложения и пользователи могут менять поведение Apache без доступа к глобальной конфигурации. Цена за гибкость — накладные расходы на каждый запрос и размытая управляемость. При включённом AllowOverride Apache проверяет наличие .htaccess во всех директориях пути к запрашиваемому файлу. Это вызывает дополнительные системные вызовы и «слипания» правил из разных уровней (директория, поддиректория), что усложняет отладку и повышает шанс ошибки конфигурации.

На реальных проектах отключение .htaccess и перенос правил в контекст VirtualHost даёт заметный выигрыш: минус 3–10 системных вызовов на статический запрос, более предсказуемое поведение mod_rewrite и централизованное управление безопасностью. В типичных CMS прирост 5–20% по RPS для статики и 2–10% по p95 для динамики (цифры зависят от диска, числа уровней каталогов и набора модулей).

Простой критерий: если вы можете править конфигурацию Apache и перезапускать службу — .htaccess вам не нужен. Отключите его и переносите логику в vhost.

Когда .htaccess всё ещё оправдан

Только когда нет доступа к конфигурации сервера: мультиарендный хостинг с изоляцией пользователей (типичный виртуальный хостинг), учебные стенды, редкие сценарии, где нельзя перезапустить Apache. В любом другом случае — переносим правила и отключаем AllowOverride. Нужен полный контроль конфигурации и модулей — переносите проект на VDS.

Краткий план миграции

  • Инвентаризация: собрать все .htaccess в DocumentRoot и подкаталогах.
  • Проверить необходимые модули: mod_rewrite, mod_headers, mod_expires, mod_access_compat или современные модули авторизации.
  • Перенести правила в VirtualHost с корректировкой синтаксиса под нужный контекст.
  • Включить централизованные политики безопасности и кеширования.
  • Прогнать проверки: configtest, тест‑запросы, логи.
  • Отключить AllowOverride (None) и удалить/заархивировать .htaccess.
  • Мониторинг после релиза: ошибки, 404/403, производительность.

Контекст имеет значение: как меняется синтаксис

Главная ловушка при переносе — различия контекста mod_rewrite:

  • .htaccess и <Directory> (per-dir): шаблоны RewriteRule матчат путь без ведущего слэша, уже «обрезанный» текущей директорией. RewriteBase может использоваться для относительных подстановок, но в <Directory> обычно не нужен.
  • Контекст сервера/VirtualHost (вне <Directory>): шаблоны матчат полный URL‑путь с ведущим / относительно DocumentRoot.

Отсюда практические правила:

  • Если пишете RewriteRule прямо в VirtualHost, добавляйте ведущий / в шаблоны.
  • Если размещаете логику внутри <Directory> (частый и удобный вариант) — используйте пер‑директориальную семантику: без ведущего слэша, RewriteBase не нужен.
  • Явно выключайте MultiViews, чтобы mod_negotiation не подменял 404.

Проверка модулей и конфигурации

apachectl -M
apachectl -t
apachectl -S

Первой командой убедитесь, что необходимый модуль включён. Второй — валидируйте синтаксис, третьей — проверьте, что правите нужный vhost.

Пример блока VirtualHost с правилами переписывания без .htaccess

Перенос типичных правил: WordPress и похожие CMS

Классические правила WordPress в .htaccess:

# .htaccess в DocumentRoot
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Эквивалент в vhost внутри <Directory>. Обратите внимание: без ведущего слэша и без RewriteBase:

# Внутри файла сайта с vhost
<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/example.com/public

    <Directory /var/www/example.com/public>
        AllowOverride None
        Options -Indexes -MultiViews
        Require all granted

        RewriteEngine On
        RewriteRule ^index\.php$ - [L]
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . index.php [L]
    </Directory>
</VirtualHost>

Альтернатива — разместить правила прямо в VirtualHost вне <Directory>, тогда шаблоны должны начинаться с /.

Laravel/Symfony: перенаправление через public/index.php

Перенос из .htaccess в vhost для приложений с фронт‑контроллером:

<VirtualHost *:80>
    ServerName app.local
    DocumentRoot /var/www/app/current/public

    <Directory /var/www/app/current/public>
        AllowOverride None
        Options -Indexes -MultiViews
        Require all granted

        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . index.php [L]
    </Directory>

    <Directory /var/www/app/current>
        # Безопасность: запрет на .env, ключи и служебные каталоги
        <FilesMatch "^\.env|composer\.(json|lock)$">
            Require all denied
        </FilesMatch>
        <DirectoryMatch ".*/(storage|bootstrap/cache|vendor)">
            Require all denied
        </DirectoryMatch>
    </Directory>
</VirtualHost>

Суть та же: AllowOverride None, чёткие границы <Directory>, защита служебных файлов и директорий.

Политики безопасности в vhost вместо .htaccess

Вместо разрозненных директив в .htaccess удобно задать централизованные правила:

  • Запрет доступа к скрытым файлам и VCS: .git, .hg, .svn, резервные файлы редакторов.
  • Перекрытие каталожного листинга: Options -Indexes.
  • Защита конфигов и секретов: .env, ключи, дампы.
  • Жёсткая схема обработки PHP: трафик только через фронт‑контроллер, нет возможности выполнять PHP в каталогах загрузок.
<VirtualHost *:80>
    ServerName secure.example
    DocumentRoot /var/www/secure/public

    <Directory /var/www/secure/public>
        AllowOverride None
        Options -Indexes -ExecCGI -Includes
        Require all granted

        <FilesMatch "^\.(?!well-known).*">
            Require all denied
        </FilesMatch>

        <FilesMatch "\.(git|hg|svn|bak|swp|old)$">
            Require all denied
        </FilesMatch>
    </Directory>

    <Directory /var/www/secure>
        <FilesMatch "^(\.env|.*\.key|.*\.pem|composer\.(json|lock))$">
            Require all denied
        </FilesMatch>
    </Directory>
</VirtualHost>

Дополнительно продумайте заголовки безопасности: Content-Security-Policy, Referrer-Policy, X-Content-Type-Options, Permissions-Policy. Подробно это разобрано в материале «гайд по заголовкам безопасности для Nginx и Apache».

Кеширование и статические ресурсы

Перенос директив кеширования из .htaccess в vhost даёт выигрыш в читаемости и исключает противоречия между подкаталогами. Базовый пример:

<VirtualHost *:80>
    ServerName static.example
    DocumentRoot /var/www/static

    <Directory /var/www/static>
        AllowOverride None
        Require all granted
    </Directory>

    ExpiresActive On
    ExpiresDefault "access plus 1h"

    <FilesMatch "\.(ico|css|js|gif|jpe?g|png|svg|webp|avif)$">
        ExpiresDefault "access plus 30d"
        Header set Cache-Control "public, max-age=2592000, immutable"
    </FilesMatch>

    <FilesMatch "\.(html)$">
        Header set Cache-Control "no-cache"
    </FilesMatch>
</VirtualHost>

Синхронизируйте версионирование файлов (суффиксы, хэши), чтобы «длинный» кеш не мешал выкатывать обновления. Дополнительно см. «стратегии Cache-Control и ETag для статики».

Схема обработки запроса Apache: проверка .htaccess против правил в vhost

Производительность: что ещё учесть при отказе от .htaccess

Миграция — хороший момент посмотреть вокруг и убрать исторические узкие места:

  • MPM и PHP: предпочтительнее связка event + PHP‑FPM вместо prefork + mod_php.
  • Сжатие: mod_deflate или brotli‑модуль, включённые в vhost.
  • HTTP/2: если используется TLS, проверьте, что сайт обслуживается с Protocols h2 http/1.1.
  • KeepAlive и таймауты: удостоверьтесь, что глобальные значения соответствуют нагрузке и профилю клиентов.
  • Логи: разнесите access/error по сайтам для удобной диагностики после миграции.

Переезд без простоя: пошагово

Рекомендуемый рабочий процесс для администратора:

  1. Собрать и сгруппировать директивы из всех .htaccess. Пометьте дубликаты и конфликтующие правила.
  2. Подготовить новый файл конфигурации сайта в каталоге sites-available (или аналогичном). Используйте отдельный блок <Directory> под DocumentRoot.
  3. Перенести rewrite, доступ и заголовки, адаптировав синтаксис под контекст vhost.
  4. Прогнать apachectl -t и включить сайт. Мягкая перезагрузка graceful минимизирует прерывания запросов.
  5. Протестировать основные маршруты и загрузку статики. Проверить 404/403, редиректы, коды кеша.
  6. Включить детализированный лог mod_rewrite на время проверки.
  7. Когда всё совпадает — ставим AllowOverride None и удаляем .htaccess (или архивируем).
apachectl -t
apachectl -S
apachectl graceful

Как включить трассировку mod_rewrite на время миграции

# Внутри нужного vhost
LogLevel warn rewrite:trace2

Не забывайте вернуть уровень логирования назад после проверки, чтобы не «шуметь» и не раздувать логи.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды Финляндия

Частые ловушки и как их избежать

  • Неправильный контекст: в <Directory> шаблоны без ведущего слэша; в VirtualHost (вне <Directory>) — с ведущим /.
  • Оставили AllowOverride не равным None: Apache продолжит читать .htaccess, и вы получите двусмысленность конфигурации.
  • Незадекларированный модуль: при миграции директив Header или Expires убедитесь, что модули подключены.
  • Исполняемый PHP в каталогах загрузок: запретите обработку PHP вне фронт‑контроллера.
  • MultiViews маскирует 404: явно выключайте при использовании mod_rewrite.

Частичный отказ: AllowOverrideList

Если по процессным причинам нужно временно оставить часть возможностей .htaccess, используйте «белый список» директив:

<Directory /var/www/site/public>
    AllowOverrideList Redirect RewriteEngine RewriteCond RewriteRule
</Directory>

Это лучше, чем общий AllowOverride All, но конечная цель — AllowOverride None и чистая конфигурация в vhost.

Проверочный чек‑лист перед отключением .htaccess

  • Главные маршруты приложения (без и с параметрами) отрабатывают корректно.
  • Редиректы: домен без www/с www, слэш в конце, HTTP→HTTPS, каноникал — все даются нужными кодами и без циклов.
  • Статика: заголовки кеша, сжатие, типы контента корректны.
  • Запрещённые файлы недоступны: .env, .git, резервные.
  • Журналы чистые: нет массовых 404/403/500, нет неожиданных переписей.
  • Нагрузочные тесты показывают ожидаемую или лучшую производительность.

Перед включением редиректа на HTTPS убедитесь, что установлены корректные SSL-сертификаты.

Итоги

Отказ от .htaccess и перенос всей логики в VirtualHost — это про производительность, безопасность и управляемость. Вы перестаёте платить за проверку конфигурации на каждый запрос, упрощаете отладку и централизуете политику доступа. Взамен — один раз продуманная миграция с корректной адаптацией контекста mod_rewrite, тестами и мониторингом. В долгосрочной перспективе это даёт меньше сюрпризов и больше RPS.

Если вы администрируете один сервер или парк проектов, заведите единые шаблоны vhost под типовые стеки (CMS, фреймворки, статика) и используйте инклюды: перенос станет повторяемым и безопасным, а .htaccess больше не будет скрытым источником задержек и уязвимостей.

Поделиться статьей

Вам будет интересно

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...