НейроАгент

Почему при редиректе добавляется /public?

Решение проблемы добавления /public при редиректе слэша в Laravel. Узнайте причины и способы исправления конфликта .htaccess файлов.

Вопрос

Почему при редиректе добавляется /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>
    

Пример:

Вопросы:

  1. Почему происходит добавление ‘/public’ в URL при редиректе со слэшем?
  2. Как исправить эту проблему, чтобы редирект корректно убирал слэш без добавления ‘/public’?
НейроАгент

Проблема с добавлением “/public” при редиректе слэша возникает из-за конфликта между двумя файлами .htaccess в вашей структуре Laravel. Это классическая проблема рекурсивного перезаписывания URL в Apache.


Содержание


Основная причина проблемы

Происходит цепочка перезаписывания URL, которая приводит к добавлению “public”:

  1. Первый редирект: При запросе https://site.ru/subdirectory/categories/category1/
  2. Перезаписывание в первом .htaccess: RewriteRule ^(.*)$ public/$1 [L] преобразует запрос во внутренний https://site.ru/subdirectory/public/categories/category1/
  3. Обработка второго .htaccess: Система обрабатывает этот путь через public/.htaccess
  4. Редирект слэша: Правило RewriteRule ^ %1 [L,R=301] видит слэш и пытается редиректить на https://site.ru/subdirectory/public/categories/category1
  5. Повторное применение правил: Этот новый запрос снова проходит через первый .htaccess, который снова добавляет “public”

Ключевая проблема: Редирект в public/.htaccess создает новый запрос, который снова обрабатывается обоими файлами .htaccess, создавая цикл и добавляя “public” к URL.


Как это исправить

Есть несколько способов исправить эту проблему:

Вариант 1: Модификация правила редиректа в public/.htaccess

Замените текущее правило редиректа слэша на:

apache
# 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 к правилу редиректа:

apache
# 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:

apache
# Закомментируйте или удалите эти строки:
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteCond %{REQUEST_URI} (.+)/$
# RewriteRule ^ %1 [L,R=301]

Альтернативные решения

Вариант 4: Изменение структуры правил в корневом .htaccess

Модифицируйте основной .htaccess, чтобы он не перезаписывал уже перезаписанные запросы:

apache
<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) для предотвращения циклов

apache
<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>

Проверка и тестирование

После внесения изменений:

  1. Очистите кеш браузера и PHP кеш (OpCache, APC и т.д.)
  2. Проверьте логи Apache для отслеживания запросов
  3. Используйте инструменты разработчика в браузере для анализа редиректов
  4. Проверьте оба сценария:
    • https://site.ru/subdirectory/categories/category1/ (должен редиректить на без слэша)
    • https://site.ru/subdirectory/categories/category1 (должен работать без редиректа)

Заключение

Проблема добавления “/public” при редиректе слэша возникает из-за конфликта между правилами перезаписывания в двух .htaccess файлах. Рекомендую использовать Вариант 1 как наиболее простое и элегантное решение - добавить условие исключения для путей, уже содержащих “public/”.

Ключевые моменты:

  • Всегда проверяйте логи Apache при работе с mod_rewrite
  • Избегайте рекурсивных перезаписываний URL
  • Используйте флаги (L, NS, CO) для контроля поведения правил
  • Тестируйте все сценарии после внесения изменений

Если проблема сохраняется, возможно стоит рассмотреть альтернативные варианты настройки или консультацию с системным администратором Apache.