Акция Панель управления ispmanager для VDS — первый месяц бесплатно
до 31.07.2026 Подробнее
Выберите продукт

Laravel на виртуальном хостинге: public/, storage:link, .env, cron и Supervisor

Разбираем запуск Laravel на виртуальном хостинге: как указать public/ в DocumentRoot или через .htaccess, закрыть .env, настроить storage:link при запрете симлинков, выбрать подходящий php-cli, совместить cron с планировщиком и очередями без Supervisor, и что делать, если нужны постоянные воркеры.
Laravel на виртуальном хостинге: public/, storage:link, .env, cron и Supervisor

Привет! Я Вася из Fastfox. В этой статье собрал практический чек-лист и разбор типичных грабель при размещении проектов на Laravel в условиях виртуального хостинга. Поговорим о том, как корректно работать с public/, обеспечить доступ к медиа через storage:link даже при запрете симлинков, закрыть .env, выбрать верный php-cli для artisan, запустить cron и настроить очереди без Supervisor. Для полноты картины покажу и эталонную схему с Supervisor для случаев, когда вы переедете на управляемую систему или облачный VDS.

Файловая структура Laravel и public dir

Идеальная схема — когда корень сайта указывает на директорию public/. Тогда PHP и веб-сервер не видят содержимого app, vendor, storage. На виртуальном хостинге у вас может быть ограничение: «корень сайта» фиксирован и указывает на корневую папку аккаунта или поддомен, и изменить путь до public/ нельзя. Есть три рабочих варианта.

Вариант 1 (лучший): сменить DocumentRoot на public/

Если панель позволяет выбрать папку сайта — укажите .../project/public. Это нивелирует проблемы с безопасностью и статикой. Дополнительно убедитесь, что в public/ есть .htaccess из коробки Laravel (он занимается маршрутизацией на index.php).

Вариант 2: переадресация в public/ через .htaccess

Если изменить корень нельзя, оставьте проект на один уровень выше, а в корне добавьте минимальные правила для переадресации всех запросов в public/:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^(.*)$ public/$1 [L,QSA]
</IfModule>

Это быстрый вариант, который хорошо работает на большинстве виртуальных тарифов. Не забывайте, что в public/ должен лежать стандартный .htaccess Laravel с правилом RewriteRule ^ index.php [L] для маршрутизации.

Коротко: если видите 404 на всех маршрутах — проверьте, что включён mod_rewrite и оба .htaccess лежат на местах.

Вариант 3 (как временная мера): перенести содержимое public/ в корень

Иногда проще переложить файлы из public/ в корень сайта и поправить пути в index.php. В оригинале там ссылки вида __DIR__.'/../vendor/autoload.php'. После переноса замените их на пути без ../:

// файл index.php после переноса содержимого public/ в корень
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';

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

storage:link на виртуальном хостинге

Команда php artisan storage:link создаёт символическую ссылку public/storage на storage/app/public. На некоторых виртуальных тарифах симлинки запрещены политикой безопасности. Попробуйте сначала:

php artisan storage:link
php artisan storage:link --relative

Если команда отработала, но статика 403/404 — возможно, в конфигурации Apache запрещены симлинки. Иногда помогает добавить в public/.htaccess:

Options +FollowSymLinks
Options +SymLinksIfOwnerMatch

Когда симлинки полностью запрещены, используйте один из обходных путей:

  • Проксируйте файлы через контроллер. Это медленнее, но работает везде.
  • Храните «общедоступные» медиа сразу в public/ (настроив файловую систему Laravel под это), если требования безопасности и регламенты проекта позволяют.

Маршрут-прокси для файлов на «паблик-диске»:

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Route;

Route::get('storage/{path}', function ($path) {
    return Storage::disk('public')->response($path);
})->where('path', '.*');

И не забудьте корректно указать FILESYSTEM_DISK=public в .env (или FILESYSTEM_DRIVER для старых версий) и опубликовать права доступа к storage/.

Пример .htaccess с переадресацией в public/

.env: безопасность и права

Файл .env должен быть недоступен из веба при любом из трёх вариантов выше. Самый простой барьер для Apache — правило в public/.htaccess или в корне сайта, если используете Вариант 2/3:

<Files ".env">
    Require all denied
</Files>

Права на .env ставьте максимально строгие: для одного пользователя под PHP-FPM обычно достаточно 600 или 640 с корректной группой. Проверьте, чтобы PHP-процесс запускался от вашего пользователя (часто так и есть на виртуальном хостинге). Для директорий storage и bootstrap/cache используйте 775, для файлов — 664. Пример команд:

cd ~/www/project
find storage bootstrap/cache -type d -exec chmod 775 {} \;
find storage bootstrap/cache -type f -exec chmod 664 {} \;

На некоторых тарифах удобнее настроить маску по умолчанию, чтобы новые файлы создавались с «дружественными» правами для веб-сервера:

umask 002

В продакшене обязательно выключите дебаг: APP_ENV=production, APP_DEBUG=false. Проверяйте, что кэш конфигурации обновлён: php artisan config:cache. И сразу включайте HTTPS: это не только про SEO и куки, но и про безопасность форм авторизации — оформите SSL-сертификаты.

php-cli: выбрать правильный бинарь для artisan и cron

Частая ловушка: веб-сервер крутится на PHP 8.2, а в консоли у вас по умолчанию PHP 7.4. В результате artisan падает на синтаксисе или несовместимых пакетах. Перед настройкой cron определите путь к нужной версии:

php -v
which php
php82 -v
/usr/local/bin/php82 -v

На большинстве панелей есть несколько алиасов: php81, php82 и т. п. Узнайте у поддержки/в панели точный путь и используйте его в заданиях cron и при запуске artisan. Примеры далее буду показывать с обобщённым /usr/bin/php — замените на свой.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Cron и планировщик Laravel

Laravel ожидает, что системный планировщик вызовет artisan schedule:run раз в минуту. На виртуальном хостинге cron обычно доступен и запускается от вашего пользователя. Минимальная запись:

* * * * * /usr/bin/php /home/user/www/project/artisan schedule:run --quiet

Чтобы исключить гонки и параллельные запуски в условиях долгих задач, добавьте блокировку с flock и заведите каталог для локов:

mkdir -p /home/user/cronlocks
* * * * * /usr/bin/flock -n /home/user/cronlocks/schedule.lock /usr/bin/php /home/user/www/project/artisan schedule:run --quiet

Дальше вся логика расписаний живёт в app/Console/Kernel.php. Старайтесь избегать длительных бесконечных процессов в schedule() — на виртуальном хостинге лучше дробить задачи и запускать их часто, с явными ограничениями по времени.

Очереди без Supervisor: рабочие паттерны

На виртуальном хостинге вы чаще всего не можете держать фоновые процессы постоянно. Но очереди нужны. Рабочие варианты:

Вариант А: драйвер database с «одноразовым» воркером

Создайте таблицу задач и переключитесь на драйвер database:

php artisan queue:table
php artisan migrate

В .env:

QUEUE_CONNECTION=database

Дальше запускайте воркера каждую минуту, ограничивая время работы и избегая параллельных копий через flock:

* * * * * /usr/bin/flock -n /home/user/cronlocks/qworker.lock /usr/bin/php /home/user/www/project/artisan queue:work database --sleep=1 --tries=3 --max-time=55 --timeout=60 --queue=default --stop-when-empty

Параметр --stop-when-empty гарантирует завершение воркера, если очередь опустела. Это удобно на виртуальном хостинге: cron сам перезапускает воркера ежеминутно.

Вариант Б: работать через schedule()

Иногда удобнее запускать воркер из планировщика, чтобы централизованно управлять логикой:

// app/Console/Kernel.php
protected function schedule(\Illuminate\Console\Scheduling\Schedule $schedule)
{
    $schedule->command('queue:work database --sleep=1 --tries=3 --max-time=55 --timeout=60 --stop-when-empty')
        ->withoutOverlapping()
        ->runInBackground()
        ->everyMinute();
}

В cron при этом достаточно одного задания schedule:run. Минус — если на тарифе жёсткие лимиты по времени выполнения, долгие бэкграунд-процессы могут быть преждевременно остановлены.

Вариант В: Redis + короткоживущие воркеры

Если тариф даёт Redis, получите меньшие накладные расходы и более стабильную обработку. В остальном схема та же: запускать короткоживущие queue:work через cron с блокировкой. Про плюсы и нюансы кешей читайте в разборе Redis и Memcached для PHP.

Не используйте бесконечные циклы while (true) на виртуальном хостинге. Их убьёт менеджер процессов/лимиты, а вы получите «залипшие» задания и непредсказуемые задержки.

Cron с schedule:run и flock на виртуальном хостинге

Supervisor и Horizon: эталон для постоянных воркеров

Когда проект вырастет и потребуется постоянный пул воркеров, входящие веб-хуки, широковещательные события — используйте Supervisor или систему инициализации (systemd/runit). На виртуальном хостинге это, как правило, недоступно, поэтому показан эталон для окружений, где вы управляете системой (например, на выделенной машине или облачном VDS):

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/project/artisan queue:work redis --sleep=1 --tries=3 --timeout=60
autostart=true
autorestart=true
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-worker.log
stopwaitsecs=3600
stopasgroup=true
killasgroup=true
environment=APP_ENV=production

Для Horizon добавляют отдельную программу с artisan horizon и таймаутами остановки. Важные практики: логируйте stdout/err, используйте stopasgroup/killasgroup, считайте, сколько воркеров нужно на ваш SLA, и держите запас по CPU/RAM.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Оптимизация билда и кэшей

Перед выкладкой на виртуальный хостинг прогоните кэши, чтобы снизить нагрузку и ускорить отклик:

php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

После обновления зависимости/конфигов не забудьте инвализировать и пересобрать кэш. Отдельно проверьте, что версия PHP-CLI совпадает с той, на которой раскладывались кэши.

.htaccess и PHP-настройки на уровне каталога

Если PHP работает через mod_php, многие параметры можно задавать в .htaccess через php_value. На PHP-FPM лучше использовать .user.ini в корне документа (обычно public/):

memory_limit=256M
upload_max_filesize=50M
post_max_size=50M
max_execution_time=60

Проверяйте, что ограничения разумны для проекта и тарифа. Для крупных загрузок и генерации миниатюр стоит предусмотреть фоновые очереди, чтобы не упираться в max_execution_time.

Диагностика типовых ошибок

  • Все маршруты отдают 404: проверьте mod_rewrite, корректность .htaccess в корне и в public/, а также права на public/index.php.
  • Скачивается файл вместо отображения страницы: у провайдера отключён PHP для каталога. Уточните обработчик PHP в панели, проверьте, что .htaccess не блокирует интерпретацию.
  • 403 при обращении к /storage/...: не работает симлинк. Проверьте storage:link, Options +FollowSymLinks, владельца/права каталогов.
  • Artisan падает на синтаксисе: в консоли другая версия PHP. Используйте явный путь к php-cli.
  • Cron «молчит»: включите логирование вывода команд во временный файл, проверьте crond у провайдера и используйте flock для борьбы с гонками.
  • Очереди стопорятся: для драйвера database проверьте индексы таблицы jobs, таймауты, количество попыток, а также блокировки транзакций в моменты пиковых вставок.

Чек-лист деплоя Laravel на виртуальном хостинге

  1. Скопируйте проект и проверьте, что корень сайта указывает на public/. Если нет — примените Вариант 2 или 3.
  2. Сгенерируйте .env, установите строгие права и запретите веб-доступ через правило Files ".env".
  3. Убедитесь, что php-cli совпадает по версии с вебом. Используйте явный путь в cron.
  4. Соберите кэши: config, route, view, event.
  5. Настройте storage:link. Если симлинки запрещены — используйте маршрут-прокси или храните публичные файлы в public/.
  6. Создайте задания cron: schedule:run раз в минуту с flock. При необходимости — «одноразовый» воркер очередей.
  7. Проверьте права на storage и bootstrap/cache, настроьте .user.ini под вашу нагрузку.
  8. Пройдитесь по логам приложения и веб-сервера после первого запуска, убедитесь, что нет 500/403/404 на статике и API.

Итоги

Laravel прекрасно работает на виртуальном хостинге, если грамотно организовать доступ к public/, закрыть .env, аккуратно обращаться с storage:link и запускать планировщик/очереди через cron. Когда проект вырастет и потребуется гарантированная обработка задач в реальном времени, переезжайте на окружение с Supervisor — это даст контролируемые долгоживущие воркеры, гибкую масштабируемость и стабильную задержку выполнения.

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

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

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...