Почти каждый, кто впервые загружает Laravel-проект на обычный хостинг, сталкивается с одной и той же картиной: все файлы скопированы в корневую папку сайта, а в браузере вместо страницы появляется список файлов, ошибка 403 или пустой экран. Причина в том, что Laravel рассчитывает на открытый доступ только к одной части проекта — папке public. Всё остальное, включая настройки, библиотеки и секретные ключи, должно лежать там, куда веб-сервер не может дотянуться. Ниже разберём, зачем нужна такая структура и как воспроизвести её на shared-хостинге.
Почему открытой должна быть только папка public
В корне Laravel-проекта находятся каталоги app, config, routes, vendor, storage и файл .env. Именно в .env хранятся пароль к базе данных, ключ шифрования APP_KEY, логин и пароль почтового сервера, секретные ключи платёжных систем. Все запросы к сайту проходят через public/index.php, который передаёт их фреймворку. Если положить весь проект в корень сайта, веб-сервер может отдать .env как обычный текстовый файл, а это означает полную утечку базы и ключей. Поэтому задача одна: при открытии домена сервер должен смотреть сразу в папку public.
Представим небольшой книжный магазин в Ташкенте, которому сделали сайт misol.uz на Laravel. Разработчик упаковал проект в ZIP, распаковал его через файловый менеджер ISPmanager в www/misol.uz, и сайт не заработал. После поспешного «исправления» адрес misol.uz/.env начал открываться в браузере вместе со всеми паролями. Описанные ниже способы как раз предотвращают такую ситуацию.
Способ 1: направить корень сайта в папку public
Самое чистое и рекомендуемое решение — изменить корневой каталог (document root) домена. В панели ISPmanager в настройках WWW-домена есть поле с корневой директорией сайта; в зависимости от версии панели его название может немного отличаться. Обычно там указан путь вида /var/www/login/data/www/misol.uz, к которому нужно дописать /public и сохранить. Файлы проекта остаются на своих местах, но из интернета доступно только содержимое public.
Преимущество этого способа в том, что Laravel работает в привычной для себя структуре и не нужно вручную править пути. Символическая ссылка от php artisan storage:link, собранные Vite файлы в build и правила перезаписи из public/.htaccess работают как обычно. Если поле недоступно для редактирования, можно создать тикет в техподдержку через кабинет sayt.uz и попросить изменить корень сайта.
Способ 2: перенаправление через .htaccess в корне
Иногда сменить корень нельзя, например когда в одном каталоге живут несколько проектов. Тогда в корень проекта кладут отдельный .htaccess, который внутренне передаёт все запросы в public. В адресной строке /public не появится, потому что это не внешний редирект, а внутренняя перезапись Apache.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
# Дополнительная защита: не отдавать служебные файлы напрямую
<FilesMatch "^(\.env.*|composer\.(json|lock)|artisan)$">
Require all denied
</FilesMatch>
Способ рабочий, но менее надёжный, чем первый. Если кто-то позже удалит этот .htaccess или изменится конфигурация сервера, весь проект снова окажется открытым. Поэтому блок FilesMatch добавлен как страховка. После загрузки обязательно откройте https://misol.uz/.env: должна появиться ошибка 403 или 404, а содержимое файла не должно показываться ни при каких условиях.
Способ 3: вынести ядро за пределы веб-каталога
Третий вариант требует больше ручной работы, но выручает там, где корень сайта поменять невозможно. Основная часть Laravel размещается в отдельной папке домашнего каталога, например laravel-app, а содержимое public копируется в www/misol.uz. Затем пути в index.php исправляются под новое расположение. Для Laravel 11 файл выглядит примерно так:
<?php
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
if (file_exists($maintenance = __DIR__.'/../../laravel-app/storage/framework/maintenance.php')) {
require $maintenance;
}
require __DIR__.'/../../laravel-app/vendor/autoload.php';
$app = require_once __DIR__.'/../../laravel-app/bootstrap/app.php';
$app->usePublicPath(__DIR__);
$app->handleRequest(Request::capture());
Строка с usePublicPath важна: без неё public_path() будет указывать на старое место, и загруженные картинки или манифест Vite не найдутся. В старых версиях index.php устроен немного иначе, поэтому берите за основу оригинальный файл своей версии и меняйте только пути. Ссылку на storage тоже придётся пересоздать: теперь она должна вести из www/misol.uz/storage в laravel-app/storage/app/public.
Vendor, версия PHP и настройки .env
На многих тарифах shared-хостинга нет SSH и Composer, поэтому папку vendor удобно собрать на своём компьютере и загрузить готовой. Делайте это в режиме для продакшена — без тестовых зависимостей и с оптимизированным автозагрузчиком:
composer install --no-dev --optimize-autoloader
npm run build
Версия PHP на хостинге не должна быть ниже той, что указана в composer.json, иначе сайт упадёт на первом же запросе. В ISPmanager версию PHP можно выбрать для конкретного домена; Laravel 11 требует как минимум PHP 8.2. В файле .env проверьте следующие значения:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://misol.uz
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=login_shop
DB_USERNAME=login_shop
DB_PASSWORD=strong-password
Особенно важен APP_DEBUG=false. Если оставить true, на любой странице ошибки будут видны пути к файлам, параметры запроса, а иногда и переменные окружения. Для атакующего это готовая карта вашего приложения.
Права доступа, кеш и финальная проверка
Laravel должен иметь право записи в storage и bootstrap/cache, иначе не будут сохраняться логи, сессии и скомпилированные шаблоны. Обычно достаточно прав 755 для папок и 644 для файлов, так как на хостинге PHP работает от имени вашего пользователя; ставить 777 не нужно и опасно. Если SSH есть, после размещения обновите кеши:
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
Помните, что после config:cache функция env() работает только внутри файлов config: вызванная напрямую в контроллере или шаблоне, она вернёт null. В конце проверьте три вещи: открывается ли главная страница, закрыт ли адрес /.env, корректно ли загружаются изображения и CSS. Если всё в порядке, проект будет безопасно и стабильно работать даже на обычном хостинге.