Переносим готовый Laravel-проект на хостинг: почему не по FTP и что сделать после загрузки
Эдгар Косулин 7 мин чтения
Серия «Хостинг без магии» · часть 17 из 17
Содержание
6 ноября клиент переносил к нам на пробный хостинг готовый интернет-магазин на Laravel, рабочий проект на несколько тысяч файлов. К вечеру сайт отдавал 500, часть файлов так и не загрузилась, а сертификат для домена не выпускался.
За один вечер собрались почти все типовые ошибки переноса Laravel. Две из них были на нашей стороне. Ниже по порядку: что случилось, почему FTP для такого проекта плохой способ и что сделать после загрузки, чтобы сайт заработал.
Почему одни и те же файлы не доходили по FTP
Клиент заливал проект FTP-программой, файл за файлом. Почти всё дошло, а шесть PHP-файлов каждый раз застревали на одном месте. Сервер получал первые 1 452 байта (у одного файла 7 260), подтверждал приём и ждал продолжения. Дальше от клиента не приходило ничего по 40-60 минут.
Программа повторяла загрузку снова и снова. В итоге три файла остались на сервере обрезанными, а один JSON-файл пустым. Обрезанный PHP-файл роняет с 500 любую страницу, которой нужен этот класс.
Почему застревали именно эти файлы, мы точно не знаем. С сервера видно, что данные не пришли, но не видно, где они остановились. Так обычно ведёт себя антивирус или фильтр на компьютере либо по пути: он проверяет передачу и задерживает файлы с определённым содержимым. Обычный FTP идёт без шифрования, и такому фильтру есть во что заглянуть.
Даже без фильтров у загрузки тысяч файлов по одному есть слабое место. На сервере оказывается то, что успело доехать. После разрыва программа может пропустить файл или оставить половину новой версии, а сообщение об этом теряется в длинном журнале. Для проекта, где один недостающий класс кладёт весь сайт, это плохой способ.
До загрузки проверьте, какой PHP в консоли
Подключитесь по SSH и выполните:
php -v
У сайта и у команды php в консоли настройки разные. У нашего клиента сайт работал на PHP 8.4, а в SSH команда php запускала 7.2. Свежие версии Laravel требуют PHP 8.2 или 8.3, так что Composer отказался бы ставить пакеты, а artisan сообщил бы, что зависимостям нужна версия новее. Версия в консоли должна совпадать с версией сайта, иначе всё дальнейшее бессмысленно.
Подробнее, почему так бывает и что ещё сверить, кроме версии, мы писали в статье Современный PHP-хостинг: зачем Laravel и Symfony нужны SSH, Composer, Node.js, cron и отдельная public-директория.
Как загрузить проект одним архивом
Надёжнее передать проект одним файлом и распаковать его на сервере. Если архив дошёл целиком, дошли и все файлы. Если нет, распаковка сразу скажет, что архив повреждён.
- Упакуйте проект в zip без папок
vendorиnode_modules: их на хостинге поставят Composer и npm, а без них архив в разы меньше. Файлыcomposer.lockиpackage-lock.jsonв архив положите обязательно. - Загрузите архив в домашнюю папку по SFTP или через файловый менеджер панели. SFTP работает поверх SSH, с теми же логином и паролем, и шифрует передачу целиком.
- Распакуйте его по SSH в папку сайта.
В Linux и macOS архив собирается так:
zip -r project.zip . -x "vendor/*" "node_modules/*" ".git/*"
На Windows то же делает любой архиватор: выделите всё, кроме двух папок. Распаковка на хостинге:
unzip -o ~/project.zip -d ~/www/example.ru
Если проект лежит в Git, архив не нужен: склонируйте репозиторий прямо на хостинге. Как на этом построить весь дальнейший деплой с откатом, мы разбирали в статье Деплой без FTP на виртуальном хостинге: Git, Composer и безопасный откат.
Удалите кеш с компьютера разработчика
В папке bootstrap/cache Laravel хранит то, что собрал на компьютере, где проект запускали. Её легко увезти вместе с проектом, и это первая причина 500 после переноса.
Файлы packages.php и services.php содержат список пакетов и их сервис-провайдеров, в том числе пакетов для разработки. На хостинге их обычно не ставят (composer install --no-dev), а Laravel при старте ищет класс из списка и не находит. Падает и сайт, и сам artisan, включая команду, которая этот список пересобрала бы. Если разработчик запускал php artisan config:cache, рядом лежит ещё и config.php с его локальными настройками базы, и .env на хостинге Laravel просто не читает.
Удалите эти файлы, Laravel создаст их заново:
cd ~/www/example.ru
rm -f bootstrap/cache/*.php
Заполните .env под хостинг
В .env после переноса обычно лежат настройки с компьютера разработчика. Для рабочего сайта нужны свои:
APP_ENV=production
APP_DEBUG=false
APP_URL=https://example.ru
DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=имя_базы
DB_USERNAME=пользователь_базы
DB_PASSWORD=пароль_базы
APP_DEBUG=false обязателен: с true страница ошибки показывает любому посетителю пути к файлам, куски кода и детали настроек. APP_URL пишите с https и новым доменом: по нему Laravel строит ссылки в письмах и везде, где адрес не берётся из запроса.
APP_KEY при переносе живого сайта оставьте прежним. Им зашифрованы сессии и всё, что проект шифровал в базе. Новый ключ нужен только новому сайту. Подробнее об этом и других настройках в статье Ошибка 500 после загрузки сайта: где искать причину до обращения в поддержку.
Сначала дамп базы, потом миграции
Если на старом сайте есть данные, импортируйте дамп в новую базу до первого запуска миграций. Миграции на пустой базе создадут таблицы, и дамп потом упрётся в «таблица уже существует». В обратном порядке всё проще: Laravel увидит в таблице migrations, что старые миграции уже выполнены, и запустит только новые.
Установите зависимости и соберите проект
Дальше в папке сайта:
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan storage:link
php artisan migrate --force
php artisan optimize
composer.lock нужен здесь. Без него Composer подберёт свежие версии пакетов в рамках composer.json, и на хостинге окажется не тот код, который проверяли у себя. По той же причине npm ci работает только с package-lock.json. Если Node.js на хостинге нет, соберите фронт у себя и загрузите готовую папку public/build.
--force у миграций нужен потому, что в режиме production Laravel спрашивает подтверждение. optimize кеширует настройки и маршруты, поэтому после любой правки .env его надо запускать заново.
Добавьте cron для планировщика и очереди
На виртуальном хостинге нет постоянно работающего обработчика очереди, поэтому и расписание, и очередь запускает cron раз в минуту:
* * * * * cd ~/www/example.ru && php artisan schedule:run >> /dev/null 2>&1
* * * * * cd ~/www/example.ru && php artisan queue:work --stop-when-empty >> /dev/null 2>&1
Без этих строк сайт откроется и будет выглядеть рабочим. Но письма, которые проект отправляет через очередь, не уйдут, а задачи по расписанию не выполнятся, и никаких ошибок на экране при этом не будет.
Почему не выпускается SSL-сертификат
У нашего клиента A-запись домена уже указывала на наш сервер, а www по-прежнему на старый хостинг. Let's Encrypt проверяет каждое имя, которое попадёт в сертификат. Проверка www не проходила, и сертификат не выдавался ни на одно из двух имён.
Лечится одной записью в DNS: A-запись www на адрес нового сервера или CNAME www на основной домен. Панель повторяет попытку выпуска сама, и после обновления DNS сертификат появляется.
Что было не так на нашей стороне
Первое касалось опции «Подготовить окружение под фреймворк». С 19 августа по 6 ноября она собирала окружение не полностью. Сайт, база и корень сайта в папке public создавались, а PHP для SSH, скрипт деплоя, файл с доступами к базе и задания cron нет. Поэтому у клиента в консоли и оказался PHP 7.2.
Эти шаги биллинг выполняет на сервере хостинга по SSH, своим ключом. Ключ был разрешён только со старого адреса биллинга, а 19 августа биллинг переехал на новый сервер. Сбой записывался в карточку заказа, но уведомления о нём не было, поэтому он долго оставался незамеченным. Теперь, если какой-то шаг не прошёл, сотрудникам сразу приходит уведомление с текстом ошибки. Двум клиентам, у которых окружение собралось не полностью, мы доделали его вручную.
Второе касалось настроек FTP. 6 ноября с 19:15 примерно до 19:52 по Москве FTP не пускал никого из клиентов. Зависшие загрузки из этой истории заняли все 20 мест, которые FTP-сервер выделял на всех сразу, а ограничения на одного клиента не было. Зависшее соединение держалось шесть часов. Это значение осталось с весенней отладки: его ставили временно и не вернули к обычному. По умолчанию такое соединение рвётся через час.
После переустановки сервера в ночь на 8 ноября зависшая передача рвётся через 5 минут, соединение без передачи через 15. С одного адреса можно открыть не больше 8 соединений, на одного пользователя не больше 10, а всего мест 60.
Как это устроено на Siteko. SSH, Composer и Node.js есть на тарифах Master и Expert. При заказе там можно включить опцию «Подготовить окружение под фреймворк»: одна версия PHP для сайта и консоли, корень сайта в
public, база с доступами в файлеDB-CREDENTIALS.txt, cron для планировщика и очереди и скриптlaravel-deploy.shв домашней папке. Скрипт выполняет установку и сборку из раздела выше, а.envи дамп базы остаются за вами. Если версия PHP в консоли не совпадает с сайтом, напишите в поддержку. Если переносить самим не хочется, пришлите архив проекта и дамп базы или доступ к репозиторию: перенос сайта к нам бесплатный.
Если после всех шагов сайт всё равно отдаёт 500, откройте storage/logs/laravel.log: там почти всегда написано, какого класса, файла или настройки не хватает. Порядок поиска по шагам есть в статье Ошибка 500 после загрузки сайта.
Серия
Хостинг без магии
- 1. Как выбрать хостинг для сайта и не купить себе вторую работу
- 2. Виртуальный хостинг, VPS или облако: что действительно нужно вашему сайту
- 3. Гигабайты ничего не решают: как читать характеристики тарифа хостинга
- 4. «Безлимитный» хостинг: что заканчивается раньше дискового пространства
- 5. Домен подключён, а сайт не открывается: DNS без мистики
- 6. HTTPS включён, а браузер всё равно ругается: SSL без паники
- 7. Письма с сайта пропадают: SMTP, SPF, DKIM и DMARC на пальцах
- 8. Современный PHP-хостинг: зачем Laravel и Symfony нужны SSH, Composer, Node.js, cron и отдельная public-директория
- 9. Деплой без FTP на виртуальном хостинге: Git, Composer и безопасный откат
- 10. Ошибка 500 после загрузки сайта: где искать причину до обращения в поддержку
- 11. Сайт тормозит на хостинге: виноват тариф, код, база или внешний сервис
- 12. Бэкап есть, восстановить нельзя: проверяем резервные копии до аварии
- 13. Переезд на другой хостинг без потери сайта, писем и заказов
- 14. Когда виртуальный хостинг стал тесен: пора на VPS или ещё можно остаться
- 15. Коды подтверждения не приходят на почту: лимит отправки писем на хостинге
- 16. Старый сайт на PHP 5.4 после переезда на MySQL 8: «Got packets out of order» и как его оживить
- 17. Переносим готовый Laravel-проект на хостинг: почему не по FTP и что сделать после загрузки
Новые статьи и разборы — в канале Siteko: Telegram MAX
Подписчикам: +30 дней к пробному хостингу, клиентам: +150 ₽ на баланс