Исправление некорректного 301 редиректа с добавлением «public» в URL

При работе с Laravel-приложением, размещенным в поддиректории, возникает проблема: при переходе на URL со слэшом на конце происходит 301 редирект, в адресную строку которого неожиданно добавляется сегмент public. Это приводит к формированию нерабочей ссылки и нарушению функциональности сайта.

Контекст проблемы

Имеется домен site.ru, в корне которого расположена директория subdirectory. В этой директории находятся файлы Laravel-приложения. Структура конфигурационных файлов .htaccess следующая:

Файл .htaccess в subdirectory/

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
  RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
  RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

Файл .htaccess в subdirectory/public/

<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, приложение работает нормально. Однако при обращении к тому же адресу со слэшом на конце (https://site.ru/subdirectory/categories/category1/) срабатывает редирект, предназначенный для удаления завершающего слэша. Вместо ожидаемого https://site.ru/subdirectory/categories/category1 браузер получает команду перенаправления на https://site.ru/subdirectory/public/categories/category1, что приводит к ошибке 404.

Причина проблемы

Конфликт возникает из-за последовательной обработки запроса двумя файлами .htaccess:

  • Первый файл (в subdirectory/) внутренне переписывает все запросы, добавляя public/ к пути.
  • Второй файл (в subdirectory/public/) выполняет 301 редирект для удаления завершающего слэша, но делает это, основываясь на уже модифицированном URL, который включает public.
  • В результате браузер получает редирект на URL с лишним сегментом public.

Решение

Необходимо предотвратить выполнение редиректа на удаление слэша в файле subdirectory/public/.htaccess для запросов, которые уже были внутренне переписаны через public. Этого можно достичь, добавив условие, проверяющее, не был ли запрос уже перенаправлен из корневой директории.

Замените блок редиректа на удаление завершающего слэша в файле subdirectory/public/.htaccess на следующий:

# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]

Ключевое изменение - добавление условия RewriteCond %{ENV:REDIRECT_STATUS} ^$. Оно проверяет, что переменная окружения REDIRECT_STATUS пуста, что означает, что запрос обрабатывается впервые и не является результатом внутренней перезаписи. Это предотвратит выполнение редиректа для URL, которые уже были модифицированы правилом RewriteRule ^(.*)$ public/$1 [L] из корневого .htaccess.

После внесения этого изменения редирект на удаление завершающего слэша будет корректно формировать конечный URL без добавления лишнего сегмента public.