Разворачиваем OpenCart 4 в продакшен: сервер, установка, русификация и ЧПУ — пошаговый регламент ITfresh

Конвейер развёртывания интернет-магазина: от стойки с сервером по ленте едут блоки nginx, PHP, базы данных и движка, на выходе — светящаяся витрина с товарами

Выбор площадки: shared, VPS или свой сервер

В обзорной статье серии я объяснял, почему мы предлагаем клиентам OpenCart. Сегодня — практика: я, как техдиректор ITfresh, покажу наш внутренний регламент развёртывания, по которому инженеры проходят путь от пустого VPS до боевой витрины. Все команды и значения ниже — не «примерные», а те, что реально стоят на магазинах, которые мы сопровождаем.

Первое решение — где магазину жить. OpenCart нетребователен, и это надо использовать, а не перестраховываться «сервером с запасом в десять раз». Наши ориентиры:

КаталогПлощадкаПочему
До 2–3 тыс. SKUshared-хостинг или минимальный VPS 1 vCPU / 1–2 ГБдвижку хватает; экономия важнее контроля
3–15 тыс. SKUVPS 2 vCPU / 4 ГБ RAM, NVMeнаш основной сценарий — весь этот регламент писан под него
От ~20 тыс. SKU, сезонные пикиVPS 4+ vCPU, БД и кэш выносим отдельноузкое место — MariaDB и генерация кэша картинок

Когда мы настаиваем именно на VPS, а не на shared: если планируется обмен с 1С (нужны свои cron и лимиты PHP), если каталог больше трёх тысяч позиций, и если клиент хочет предсказуемости — на shared сосед по серверу может в любой день устроить вам деградацию, а поддержка хостера разведёт руками. Свои магазины мы размещаем на собственных серверах в дата-центре МТС: гипервизор наш, диски наши, и когда клиент звонит «сайт тормозит», мы смотрим метрики, а не пишем тикет чужой поддержке.

Операционная система — Ubuntu 24.04 LTS. Не потому, что она чем-то волшебна, а потому, что в её штатных репозиториях лежит PHP 8.3, поддержка до 2029 года, и весь наш парк унифицирован: один регламент обновлений на все машины. Дальше по тексту все команды — под неё.

Готовим стек: nginx + PHP-FPM 8.3 + MariaDB

Официальный минимум для четвёртой ветки — PHP 8.1, а релиз 4.1.0.4 от 11 августа 2026 года официально дружит и с PHP 8.5. Мы ставим 8.3 из штатного репозитория Ubuntu 24.04: без сторонних PPA, обновления приходят вместе с системой. Гнаться за 8.5 на бою пока незачем — сторонние модули под неё ещё не все причёсаны.

Пакеты и PHP-модули

Инсталлятор OpenCart на втором шаге сам проверяет расширения и не пустит дальше, если чего-то нет. Обязательный список: cURL, GD, ZIP, Zlib, mbstring, OpenSSL плюс драйвер MySQL. Ставим всё разом:

apt update
apt install nginx mariadb-server \
  php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd \
  php8.3-zip php8.3-mbstring php8.3-xml php8.3-intl
mysql_secure_installation

php8.3-mysql приносит и mysqli, и pdo_mysql — инсталлятору хватит любого; intl и xml формально не обязательны, но без них спотыкаются половина модулей, так что мы включили их в стандарт. OpenSSL и Zlib в убунтовской сборке PHP уже вкомпилированы.

Server-блок nginx

OpenCart из коробки заточен под Apache с .htaccess, где правило ЧПУ переписывает всё на index.php?_route_=…. Под nginx это одна строка в try_files. Наш боевой шаблон, сокращённый до сути:

server {
    listen 80;
    server_name shop.example.ru;
    root /var/www/shop/public;
    index index.php;
    client_max_body_size 64m;

    location / {
        try_files $uri $uri/ /index.php?_route_=$uri;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
    # служебные каталоги наружу не отдаём
    location ~ ^/(system|storage)/ { deny all; }
    location ~ /\. { deny all; }

    location ~* \.(webp|jpg|png|svg|css|js|woff2)$ {
        expires 30d;
        access_log off;
    }
}

client_max_body_size 64m — не прихоть: через админку загружаются архивы модулей и пачки изображений, и дефолтный 1 МБ nginx режет их с ошибкой 413. Запрет на system/ обязателен: там лежат логи и код, которым в браузере делать нечего.

Тюнинг PHP-FPM и OPcache

Дефолтный пул FPM рассчитан на «сайт-визитку». Для каталога 5–10 тыс. SKU на VPS 2 vCPU / 4 ГБ мы ставим в /etc/php/8.3/fpm/pool.d/www.conf:

pm = dynamic
pm.max_children = 12
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500

Арифметика простая: воркер OpenCart под нагрузкой ест 60–90 МБ, двенадцать воркеров — около гигабайта, ещё гигабайт-полтора оставляем MariaDB, остальное — системе и файловому кэшу. Поставите max_children «побольше, чтоб было» — под пиком сервер уйдёт в swap, и это хуже, чем очередь запросов. pm.max_requests = 500 страхует от утечек памяти в кривых модулях: воркер перезапускается сам.

В /etc/php/8.3/fpm/conf.d/99-opcache.ini:

opcache.memory_consumption = 192
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 1
opcache.revalidate_freq = 60

У OpenCart 4 с Twig-шаблонами и модулями набегают тысячи PHP-файлов — дефолтных 128 МБ кэша опкодов впритык, 192 МБ хватает с запасом. Timestamps мы не отключаем: выигрыш микроскопический, а забытый сброс кэша после правки модуля стоил одному нашему инженеру часа недоумения.

Архитектура стека магазина слоями: браузер, nginx с TLS, PHP-FPM, движок OpenCart и MariaDB; каталог storage вынесен за пунктирную границу webroot

Установка: от архива до мастера

Релизы живут на GitHub в репозитории opencart/opencart — оттуда и берём, никаких «сборок с файлопомоек». Скачиваем архив свежего релиза 4.1.0.4, содержимое каталога upload/ кладём в webroot и сразу активируем конфиги-заготовки:

cd /var/www/shop
unzip opencart-4.1.0.4.zip
mv upload public
cd public
mv config-dist.php config.php
mv admin/config-dist.php admin/config.php
chown -R www-data:www-data /var/www/shop

База и пользователь — с минимальными правами и только с localhost. Права SUPER, FILE и прочая экзотика магазину не нужны, а при взломе сайта урезанный пользователь БД сильно ограничивает ущерб:

CREATE DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'shop'@'localhost' IDENTIFIED BY 'длинный_пароль';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER,
      INDEX, DROP, LOCK TABLES ON shop.* TO 'shop'@'localhost';

Дальше — веб-инсталлятор: открываем сайт в браузере, соглашаемся с лицензией, проходим проверку расширений (если готовили сервер по прошлому разделу — она зелёная), вводим реквизиты БД и создаём администратора. Пять минут.

Пост-шаги, без которых в бой нельзя

Инсталлятор закончился — начинается настоящая работа. Три обязательных действия:

  • Удалить /install. Оставленный каталог инсталлятора — классическая дыра: любой желающий может «переустановить» вам магазин. OpenCart 4 сам напоминает об этом баннером — не игнорируйте.
  • Вынести storage/ за пределы webroot. Там лежат сессии, кэш, логи и загруженные файлы. Четвёрка предлагает перенос прямо в админке кнопкой; мы переносим в /var/www/shop/storage/ — на уровень выше public. После переноса пути в обоих config.php обновляются автоматически, но проверить константу DIR_STORAGE глазами — входит в наш чек-лист. Даже если когда-нибудь сломается запрет в nginx, файлы физически недостижимы по HTTP.
  • Переименовать каталог админки. /admin — первое, куда стучатся брутфорс-боты. Переименовываем каталог во что-то своё (условно /backoffice-x7) и правим пути в admin/config.php — там константы с путями и URL админки указаны явно, меняются заменой строки.

Грабли первого запуска

Права на запись. Самая частая проблема: картинки не загружаются, кэш не пишется. Каталоги image/ (вместе с image/cache/) и вынесенный storage/ должны принадлежать пользователю, под которым работает FPM: chown -R www-data:www-data плюс права 755 на каталоги и 644 на файлы. Ставить 777 «чтобы заработало» — запрещено нашим регламентом: это подарок первому же залитому шеллу.

Белый экран. Пустая страница после установки почти всегда означает фатальную ошибку PHP при выключенном выводе ошибок, и в девяти случаях из десяти — отсутствующее расширение, которое понадобилось модулю уже после инсталлятора. Диагноз ставится за минуту по логам: tail -f /var/log/php8.3-fpm.log и лог OpenCart в storage/logs/error.log. Приучите себя начинать с логов, а не с гадания — сэкономите часы.

Русификация: пакет на чистый OpenCart или сборка ocStore

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

Путь 1: чистый OpenCart + языковой пакет

Берём бесплатный русский языковой пакет под свою ветку (для 4.x они живут в официальном маркетплейсе и на профильных русскоязычных форумах), загружаем через админку: Extensions → Installer, затем в System → Localisation → Languages добавляем русский с кодом ru-ru. Дальше в настройках магазина переключаем язык витрины и админки по умолчанию. Отдельно проверьте валюту (рубль с правильным форматом), часовой пояс и список стран в форме заказа: покупателю из Тулы незачем листать выпадашку из двухсот государств.

Путь 2: сборка ocStore

ocStore — это тот же OpenCart, но с русским языком, ЧПУ и модулями под РФ из коробки. Ставится так же: архив, инсталлятор, те же пост-шаги. Существенный нюанс 2026 года: актуальный ocStore — это ветка 3.0.4.x, то есть по коду он следует за «тройкой», а не за четвёркой. Выбирая сборку, вы выбираете и legacy-ветку — живую и поддерживаемую (3.0.5.1 вышла в один день с 4.1.0.4), но архитектурно вчерашнюю.

Как выбираем мы

Сборка быстрее на старте: день экономии на русификации и типовых модулях — честная цифра из нашей практики. Но чистое ядро проще обновлять: между вашим магазином и апстримом нет чужой прослойки, каждый релиз OpenCart накатывается по официальной процедуре, а не «ждём, пока авторы сборки перенесут». Поэтому наше правило: если магазин строится на горизонт три года и больше — чистый OpenCart 4.x с языковым пакетом; сборка — когда дедлайн жёсткий, магазин типовой и заказчик осознанно принимает ветку 3.0.x. Обе конфигурации мы сопровождаем, обе рабочие — важно лишь, чтобы выбор был сделан с открытыми глазами, а не «так поставил прошлый подрядчик».

Развилка дороги при русификации: левый путь — чистый OpenCart с языковым пакетом, правый — готовая сборка ocStore с предустановленными модулями

ЧПУ, SSL и базовое SEO при запуске

Магазин установлен и говорит по-русски — доводим URL и шифрование до боевого состояния.

ЧПУ. В настройках магазина (вкладка «Сервер») включаем Use SEO URLs. Дальше главное: у каждой категории, товара и информационной страницы должен быть заполнен SEO keyword — на четвёрке они редактируются в разделе Design → SEO URL централизованно. Два классических фейла, которые мы разгребали за другими: включили ЧПУ, а правило перезаписи на сервере не настроено — вся витрина отдаёт 404 (в нашем nginx-конфиге из второго раздела строка try_files … /index.php?_route_=$uri ровно про это); и неуникальные keyword у двух товаров — движок начинает отдавать не тот товар или ошибку. Правило: ЧПУ включается один раз перед запуском, до индексации, а не «потом как-нибудь».

SSL. Сертификат — Let's Encrypt через certbot. Грабля, на которую наступает каждый второй: инструкции из интернета предлагают certbot certonly --standalone, а standalone-режим поднимает собственный веб-сервер на 80-м порту — который уже занят вашим nginx, и certbot падает. Правильно — плагин nginx:

apt install certbot python3-certbot-nginx
certbot --nginx -d shop.example.ru --redirect

Плагин сам дополнит server-блок, выпустит сертификат, настроит 301-редирект с http на https и создаст таймер автопродления. После этого обязательно: в config.php и admin/config.php меняем константы HTTP-адресов на https-вариант — иначе часть ссылок и картинок останется незашифрованной и браузер будет ругаться на mixed content.

Минимальная SEO-гигиена. Перед открытием индексации: robots.txt с запретом служебных маршрутов (поиск, сортировки, сравнение — генераторы дублей) и ссылкой на карту сайта; штатный sitemap OpenCart включается как модуль фида и отдаёт актуальный XML сам. Про борьбу с дублями страниц и канониклы я подробно предупреждал в обзоре — здесь зафиксирую вывод: закладывайте SEO-модуль в смету сразу, при запуске это час работы, а после индексации кривых URL — недели переклейки редиректов.

Наполнение: импорт каталога, не убивая сервер

Витрина готова — пора заливать каталог. Руками через админку реально завести пару сотен товаров; на наших типовых пяти-десяти тысячах SKU нужен импорт.

Штатного полноценного импорта из CSV/Excel в ядре нет — это делается модулем (классика жанра — расширения типа Export/Import по XLSX-шаблону) либо, если источник — 1С, сразу модулем обмена, но это тема следующей статьи серии. Наш порядок при первичной загрузке:

  1. Сначала дерево категорий, потом товары — иначе половина позиций ляжет мимо разделов.
  2. Изображения заливаем не через веб-форму, а пакетно по SFTP в image/catalog/, в импорт-файле указываем относительные пути. Загрузка тысяч картинок через браузер — мучение и таймауты.
  3. Импорт гоняем частями по 500–1000 позиций и первый раз — на тестовой копии: одна кривая колонка в прайсе на десятой тысяче строк — и вы разбираетесь, что успело залиться, а что нет.

Лимиты PHP на время импорта. Дефолтные max_execution_time = 30 и memory_limit = 128M большой импорт не переживёт — скрипт умрёт на середине. На время загрузки поднимаем в пуле FPM лимиты до 300 секунд и 512 МБ, после импорта возвращаем обратно. Именно возвращаем: щедрые лимиты в проде маскируют проблемные модули, которые мы предпочитаем видеть в логах сразу.

Кэш изображений. Важная особенность, которую надо знать: OpenCart не хранит готовые превью — он генерирует их при первом обращении и складывает в image/cache/. Поэтому сразу после импорта витрина будет ощутимо тормозить: каждый первый показ категории — это ресайз десятков картинок силами GD. Это не поломка, это холодный кэш. Мы прогреваем его до анонса — обходчиком по собственной карте сайта:

wget -q --spider --recursive --level=3 \
  --wait=0.5 https://shop.example.ru/

Полчаса-час фоновой прогулки по сайту — и к приходу живых покупателей все превью уже сгенерированы. Заодно такой обход — бесплатный смоук-тест: 404 и 500 в логе wget всплывают до того, как их увидит клиент.

Чек-лист перед запуском в бой

Финальный проход по нашему внутреннему чек-листу — инженер отмечает пункты письменно, «на глазок» не принимается:

  1. Каталог /install удалён, админка переехала со стандартного пути, у администраторов — длинные уникальные пароли.
  2. storage/ вынесен за webroot, права www-data проверены, в nginx закрыты system/ и дот-файлы.
  3. Вывод ошибок на экран выключен (display_errors = Off в FPM и «показывать ошибки» снято в настройках магазина), запись в лог — включена. Трейс ошибки на витрине — это и стыдно, и утечка путей сервера.
  4. Режим maintenance использовали по назначению: всё время наполнения магазин стоял закрытым от посетителей и роботов, и открываем его только этим пунктом, а не «он с первого дня торчал наружу пустым».
  5. Тестовый заказ по полному циклу: корзина → оформление → оплата тестовым платежом → смена статусов в админке → письма покупателю и менеджеру дошли. Не «страницы открываются», а именно сквозной заказ.
  6. Почта — через SMTP, а не через php mail(): в настройках магазина указываем SMTP-реквизиты ящика на домене. Письма с VPS через mail() без SPF/DKIM почти гарантированно едут в спам, а «клиент не получил подтверждение заказа» — худшая первая жалоба.
  7. Первичный бэкап снят и проверен: дамп MariaDB плюс файлы (webroot и вынесенный storage) уехали на другой сервер, и — ключевое — из этого бэкапа развёрнута контрольная копия. Непроверенный бэкап равен его отсутствию.
  8. Мониторинг подключён: у нас это Zabbix — доступность витрины по https, срок сертификата, место на диске, размер лога ошибок. Про алёрты и регламент эксплуатации будет отдельная статья серии.

Сколько это всё занимает. Типовое развёртывание по описанному регламенту — рабочий день-полтора инженера: два-три часа на сервер и стек, час на установку с пост-шагами, остальное — русификация, ЧПУ, SSL и прогон чек-листа. Отдельными строками в смете идут наполнение каталога (целиком зависит от качества исходного прайса — от пары часов до нескольких дней) и интеграции: платёжка, доставка, обмен с 1С. Про интеграции — в следующей статье; там же разберём переезд на OpenCart с других CMS.

Развернём магазин на OpenCart под ключ

ITfresh — IT-аутсорсинг для компаний до 50 рабочих мест в Москве. Поднимем VPS, развернём и русифицируем OpenCart по этому регламенту, настроим ЧПУ, SSL и бэкапы — и возьмём магазин на сопровождение вместе с остальной IT-инфраструктурой. Собственные серверы в дата-центре МТС, 15+ лет опыта. Telegram: @ITfresh_Boss, телефон: +7 903 729-62-41

📞 Связаться с нами
#OpenCart #интернет-магазин #nginx #PHP-FPM #VPS #ocStore #развёртывание #ИТ-аутсорсинг
Комментарии 0

Оставить комментарий

загрузка...

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Письмо придёт в течение минутыНе нашли его во «Входящих» — загляните в папку «Спам» или «Промоакции» и нажмите «Не спам». Так все следующие выпуски будут приходить прямо в основную почту.
Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.