Почему при редиректе добавляется /public?
Проблема: при переходе на URL со слэшом на конце в URL 301 редиректа добавляется ‘public’, что приводит к ошибке. Как это исправить?
Ситуация:
- Домен: site.ru
- Директория: subdirectory (содержит файлы Laravel)
- В subdirectory/.htaccess:apache
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301] RewriteRule ^(.*)$ public/$1 [L] </IfModule> - В subdirectory/public/.htaccess:apache
<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews -Indexes </IfModule> RewriteEngine On # Internally rewrite Filament assets to prefixed path RewriteRule ^subdirectory/css/filament/(.*)$ css/filament/$1 [L] RewriteRule ^subdirectory/js/filament/(.*)$ js/filament/$1 [L] # Handle Authorization Header RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] # Redirect Trailing Slashes If Not A Folder... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] # Send Requests To Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] </IfModule>
Пример:
- Корректный URL без слэша: https://site.ru/subdirectory/categories/category1 (работает)
- Проблемный URL со слэшем: https://site.ru/subdirectory/categories/category1/
- Ожидаемый редирект: https://site.ru/subdirectory/categories/category1
- Фактический редирект: https://site.ru/subdirectory/public/categories/category1 (ошибка)
Вопросы:
- Почему происходит добавление ‘/public’ в URL при редиректе со слэшем?
- Как исправить эту проблему, чтобы редирект корректно убирал слэш без добавления ‘/public’?
Проблема с добавлением “/public” при редиректе слэша возникает из-за конфликта между двумя файлами .htaccess в вашей структуре Laravel. Это классическая проблема рекурсивного перезаписывания URL в Apache.
Содержание
- Основная причина проблемы
- Как это исправить
- Альтернативные решения
- Проверка и тестирование
- Заключение
Основная причина проблемы
Происходит цепочка перезаписывания URL, которая приводит к добавлению “public”:
- Первый редирект: При запросе
https://site.ru/subdirectory/categories/category1/ - Перезаписывание в первом .htaccess:
RewriteRule ^(.*)$ public/$1 [L]преобразует запрос во внутреннийhttps://site.ru/subdirectory/public/categories/category1/ - Обработка второго .htaccess: Система обрабатывает этот путь через
public/.htaccess - Редирект слэша: Правило
RewriteRule ^ %1 [L,R=301]видит слэш и пытается редиректить наhttps://site.ru/subdirectory/public/categories/category1 - Повторное применение правил: Этот новый запрос снова проходит через первый .htaccess, который снова добавляет “public”
Ключевая проблема: Редирект в public/.htaccess создает новый запрос, который снова обрабатывается обоими файлами .htaccess, создавая цикл и добавляя “public” к URL.
Как это исправить
Есть несколько способов исправить эту проблему:
Вариант 1: Модификация правила редиректа в public/.htaccess
Замените текущее правило редиректа слэша на:
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteCond %{REQUEST_URI} !^public/ [NC]
RewriteRule ^ %1 [L,R=301]
Что это делает: Добавляет условие RewriteCond %{REQUEST_URI} !^public/ [NC], которое предотвращает применение редиректа, если URL уже содержит “public/”.
Вариант 2: Использование флага NS (No Subrequest)
Добавьте флаг NS к правилу редиректа:
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301,NS]
Что это делает: Флаг NS предотвращает применение правила к внутренним подзапросам, которые создаются правилом перезаписывания в родительском .htaccess.
Вариант 3: Удаление редиректа слэша из public/.htaccess
Если редирект слэша не критичен, можно просто удалить его из public/.htaccess:
# Закомментируйте или удалите эти строки:
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteCond %{REQUEST_URI} (.+)/$
# RewriteRule ^ %1 [L,R=301]
Альтернативные решения
Вариант 4: Изменение структуры правил в корневом .htaccess
Модифицируйте основной .htaccess, чтобы он не перезаписывал уже перезаписанные запросы:
<IfModule mod_rewrite.c>
RewriteEngine On
# Перенаправление с www на без www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
# Исключаем уже перезаписанные запросы
RewriteCond %{REQUEST_URI} !^public/
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
Вариант 5: Использование флага CO (Cookie) для предотвращения циклов
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
# Устанавливаем cookie для предотвращения циклов
RewriteCond %{HTTP_COOKIE} !^.*laravel_skip_redirect.*$
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Добавляем cookie для внутренних запросов
RewriteCond %{REQUEST_URI} ^public/
RewriteRule .* - [CO=laravel_skip_redirect:1:.site.ru]
# ... остальные правила ...
# Redirect Trailing Slashes
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
</IfModule>
Проверка и тестирование
После внесения изменений:
- Очистите кеш браузера и PHP кеш (OpCache, APC и т.д.)
- Проверьте логи Apache для отслеживания запросов
- Используйте инструменты разработчика в браузере для анализа редиректов
- Проверьте оба сценария:
https://site.ru/subdirectory/categories/category1/(должен редиректить на без слэша)https://site.ru/subdirectory/categories/category1(должен работать без редиректа)
Заключение
Проблема добавления “/public” при редиректе слэша возникает из-за конфликта между правилами перезаписывания в двух .htaccess файлах. Рекомендую использовать Вариант 1 как наиболее простое и элегантное решение - добавить условие исключения для путей, уже содержащих “public/”.
Ключевые моменты:
- Всегда проверяйте логи Apache при работе с mod_rewrite
- Избегайте рекурсивных перезаписываний URL
- Используйте флаги (L, NS, CO) для контроля поведения правил
- Тестируйте все сценарии после внесения изменений
Если проблема сохраняется, возможно стоит рассмотреть альтернативные варианты настройки или консультацию с системным администратором Apache.