Інтернет-магазини · OpenCart · WooCommerce · Laravel · Україна

Від правки на годинудо магазину з нуля.

Роблю нові інтернет-магазини, доробляю наявні й рятую ті, що почали падати. Обсяг залежить від того, що вам потрібно зараз і що ви зараз можете — від однієї правки до всього проєкту.

Спершу замір, потім правка

Сторінка категорії, повна відповідьнайпопулярніша сторінка · 19 306 заходів за місяць
до2 205 мс
після292 мс
−87%
Магазин на 24 579 товарів, у якого сторінки категорій відкривались понад дві секунди. Причину знайдено в базі, робота зайняла один день. Так виглядає більшість моїх звітів: цифра до, цифра після і пояснення, чому саме так.
Роботи

Магазини, з якими я працював

Понад 120 проєктів за весь час

Назв клієнтів не показую, тому знімки — це сторінки каталогу, а не головні: видно фільтри, сітку товарів і ціни, тобто саме те, за чим покупець судить магазин. Під кожним написано, що там було зроблено.

  • Сторінка каталогу книжкового магазину: фільтри за автором, мовою й роком, сітка товарів із цінами
    Швидкість

    Каталог на 24 579 товарів, який падав на кожному сьомому запиті

    Знайдено причину помилок, почищено базу, додано індекси, переписано вибірку товарів у категорії.

    Категорії 2 205 → 292 мс · база 2,5 ГБ → 300 МБ

  • Сторінка каталогу магазину фурнітури для рукоділля
    Інтеграція

    Власний модуль Нової пошти

    Вибір відділення просто в оформленні замовлення, з підстановкою адреси в саме замовлення.

  • Сторінка каталогу магазину продуктів здорового харчування
    Діагностика

    Хто саме вимикає товари в каталозі

    Розбір 319 044 рядків логів доступу за місяць і звірка з базою, щоб знайти джерело змін.

  • Сторінка каталогу магазину електроніки з сіткою товарів
    Оптимізація

    Магазин, який упирався в квоту хостингу

    Стиснуто 74 649 оригіналів і близько 83 000 мініатюр, плюс правка движка, щоб кеш не ріс назад.

    Звільнено 19,8 ГБ · тариф міняти не довелось

  • Сторінка каталогу магазину сільгосптехніки
    Інтеграція

    Імпорт каталогу з чужої платформи

    Перенесення товарів і категорій за розкладом, із захистом від дублів на повторному запуску.

  • Сторінка каталогу магазину вентиляційного обладнання
    Підтримка

    Мультимовний магазин із 21 активною модифікацією

    Постійна підтримка: SEO-модулі, мультимовність і сумісність правок між собою.

  • Сторінка магазину шин на WooCommerce
    Доопрацювання

    Магазин шин на WooCommerce

    Правки каталогу й мобільної версії, аудит усіх сторінок, іконки та сторінки послуг.

  • Сторінка каталогу магазину автозапчастин
    Інтеграція

    Вихід на маркетплейс

    Генерація XML-фіда під вимоги майданчика, мапінг категорій і атрибутів каталогу.

З чим приходять

П’ять речей, які можна мені віддати

Новий магазин з нуля

Магазину ще немає — або наявний простіше зробити заново, ніж далі лагодити.

Магазин на OpenCart чи WooCommerce: каталог, фільтри, доставка й оплата, вивантаження на маркетплейси, зв’язок із CRM. Починаємо з письмового ТЗ, яке ви погоджуєте до початку, щоб на середині ніхто нічого не вгадував.

Доопрацювання наявного

Магазин працює, але чогось бракує: функції, зручності, швидкості для ваших менеджерів.

Нові розділи й функції, переробка кошика чи оформлення, правки дизайну, зручніша адмінка. Можна взяти одну задачу, можна працювати постійно. Це найчастіший формат: більшість магазинів не треба переробляти — треба доробити.

Магазин почав гальмувати

Сторінки відкриваються по кілька секунд. Оформлення замовлення тягнеться. Звіт про швидкість у Google почервонів.

Я заміряю, перш ніж щось міняти, — щоб робота пішла туди, де справді втрачаються секунди, а не туди, де щось виглядає підозріло. Ви отримуєте цифри до і після, а не запевнення, що тепер наче швидше.

Магазин зламали

Дивні сторінки у видачі Google, лист від хостера або відвідувачів кудись перекидає.

Почистити файли — легша половина. Робота в тому, щоб знайти, як зайшли, і закрити цей шлях: чистка без цього тримається рівно доти, доки не повернуться тим самим способом. Ви отримуєте названу точку входу, а не просто прибрані наслідки.

Фіди, CRM та інтеграції

Розміщення на маркетплейсах, CRM, ціни й залишки, які хтось досі оновлює руками.

Фіди для Rozetka і Prom, синхронізація з CRM, імпорт за розкладом. Зроблений так, щоб другий запуск оновлював каталог, а не мовчки його дублював — саме на цьому врешті ламається більшість імпортів.

Як працюю

Три кроки, і жодного сюрпризу в рахунку

Однаково і для нового магазину, і для однієї правки. Різниця лише в тому, скільки триває кожен крок.

  1. 01

    Діагностика

    Для наявного сайту — пошук справжньої причини, а не здогади: я дивлюсь на живу систему, логи й базу і кажу, що там насправді відбувається. Для нового магазину це той самий крок, лише спрямований уперед: письмове ТЗ, де видно, що саме робимо, з чим інтегруємось і що буде в адмінці.

  2. 02

    Оцінка

    Обсяг і строк — до початку робіт, а не після. Якщо в процесі стає ясно, що задача більша, ніж виглядала, ви дізнаєтесь про це тоді ж, а не з рахунку в кінці.

  3. 03

    Робота

    Бекап перед кожною правкою на живому сайті. Заміри до і після там, де їх взагалі можна зробити. У кінці — короткий звіт, що саме змінено й де воно лежить, щоб ви не залежали від мене довше, ніж самі хочете.

Про гроші: оцінка фіксується до початку робіт. Великий обсяг можна розбити на етапи — почати з найболючішого і рухатись далі, коли буде можливість. Задач, «надто дрібних, щоб писати», не буває: година роботи — теж робота.

Роботи

Чотири роботи, з цифрами

Назви клієнтів не розкриваються. Цифри заміряні на живих системах, до і після.

01Маркетингова агенція · внутрішня CRM · Laravel + React

CRM, зібрана з прототипу в робочу систему.

  • Розробка
  • Laravel
  • React

В агенції були намальований прототип CRM і жива версія, яка на нього не була схожа. Клієнт очікував, що доведеться переписувати все з нуля. Переписувати не довелось: основна розбіжність трималась на порядку підключення стилів. Далі була звичайна робота — 21 екран, 12 вкладок звітів, 8 розділів фінансів, канбан із перетягуванням, глобальний пошук, експорт, редагування просто в картці.

Як саме це знайдено й зроблено

Симптом

Клієнт сформулював це так: жива CRM далека від прототипу, по всіх сторінках і блоках. У кімнаті очікували переписування з нуля.

Заміряно

Компоненти були правильні. Неправильним був порядок стилів: дизайн-кіт підключався перед Tailwind, тому preflight обнуляв усі заголовки, а один клас на body перебивав шрифт кіта. Дві структурні правки повернули більшість екранів на місце й звели реальну різницю до обсягу робіт.

Причина

Усе, що прототип імітував у сховищі браузера, переїхало в справжні таблиці: комерційні пропозиції, доступи клієнта з шифрованими секретами, журнал активності компанії, договори, журнал дзвінків. Плюс те, за що прототипу ніколи не доводиться відповідати: пікер періоду керує потоками, але свідомо не чіпає знімки стану, бо «прострочені рахунки станом на квітень» — не те, з чим можна щось зробити сьогодні.

02Магазин спорттоварів і шахів · OpenCart 3 · 24 579 товарів

Кожен сьомий запит падав. Полагоджено за день.

  • Швидкість
  • OpenCart 3
  • Один день

Магазин віддавав помилку приблизно на кожному сьомому запиті, і щотижня частіше. Хостер сказав «ви впираєтесь у ліміт» і на цьому зупинився. Справжня причина була в одній службовій таблиці, яку три роки ніхто не чистив: вона розрослась до 89% усієї бази. За день база схудла з 2,5 ГБ до 300 МБ, сторінки категорій прискорились усемеро, помилки зникли. Магазин працював увесь час, поки я це робив.

Найпопулярніша сторінка категорії
до2 205 мс
після292 мс
−87%
Помилки HTTP 500 за 90 хвилин
до825
після0
−100%
Як саме це знайдено й зроблено

Симптом

Близько 15% запитів віддавали HTTP 500 — 825 з них за півтори години спостереження. Кількість подвоювалась щотижня: 17 фатальних помилок 4 серпня, 413 — 18-го, 915 — 21-го. Хостер вказав на ліміт з’єднань і на цьому зупинився.

Заміряно

Кожна фатальна помилка була одним і тим самим рядком: з’єднання з базою всередині обробника сесій. Хостер підтвердив, що акаунт упирається у стелю в 30 одночасних з’єднань.

Таблиця сесій містила 18 715 802 рядки й 2 284 МБ — 89% усієї бази. Сесії з 2023 року досі лежали в ній.

Кожен запит, включно з кожним ботом, пише в цю таблицю. На 2,3 ГБ кожен запис тримає з’єднання достатньо довго, щоб тридцять із них наклались. Сам сервер був здоровий: 40 ядер під навантаженням 40%, вільних 67 ГБ RAM. Сусіди по шареду ні до чого.

Причина

OpenCart 3 порівнює int із колонкою datetime у збирачі сміття сесій, тому запит на чистку не збігався ніколи — жодного разу за три роки. Сесії просто накопичувались, доки ліміт з’єднань не зробив решту.

Результат

База даних: 2 562 МБ → 301 МБ. Таблиця сесій: 2 284 МБ → 22 МБ. Головна сторінка: 370 мс → 116 мс. 22 839 142 рядки видалено за 17 хвилин, порціями по 5 000 з паузою між ними — load average сервера під час чистки не виріс, а впав.

03Магазин картин і фотошпалер

Сторінку казино бачив лише Googlebot.

  • Безпека
  • OpenCart

Власник нічого не помічав, і жодна перевірка нічого не знаходила: відвідувачам показувався справжній магазин. А Google у цей час бачив на головній турецьку сторінку казино і саме її індексував. Зараза ховалась від усіх відомих сканерів навмисне. Я вимкнув підміну однією дією, потім знайшов у логах, як саме зайшли, — і закрив цей шлях у п’яти місцях, а не в одному.

Як саме це знайдено й зроблено

Симптом

Ззовні все було гаразд. Відвідувачі бачили справжній магазин. Так само його бачили curl, PageSpeed, Ahrefs, монітор аптайму й кожен сканер, який пробував власник. А тим часом головну переіндексовували як турецьку гемблінг-сторінку — з canonical, що вказував назад на домен клієнта.

Заміряно

Клоак мав список декой-юзерагентів — curl, wget, python, ahrefsbot, semrushbot, lighthouse, pagespeed, gtmetrix, uptimerobot, gptbot — і для кожного з них вимикався. Саме тому жодна перевірка нічого не знаходила.

index.php показував час зміни серпня 2025 року, а ctime — 1 вересня 2026-го. Зловмисник повернув mtime через touch; ctime без root підробити неможливо. Саме ця розбіжність і видала файл — пошук за датою модифікації пройшов би повз.

Закріплень було два, а не одне: інжект у index.php і .user.ini з auto_prepend_file, який PHP підвантажує до кожного запиту в теці незалежно від того, що написано в index.php.

Причина

Лог доступу назвав причину точно. Контролер теми виконував unserialize(base64_decode($_POST[…])) — PHP object injection, у файлі від 2022 року, який ніколи не патчили. Сканер перебрав відомі вразливі ендпоінти чужих тем OpenCart, зібрав по 404 з кожного, влучив у цей — і мав веб-шел через 24 секунди після першого запиту. Той самий рядок був у п’яти місцях, і одне з них перекривала ocmod-збірка: правка лише оригіналу залишила б найнавантаженіший ендпоінт відчиненим.

Результат

Перевірено ззовні: до чистки Googlebot отримував сторінку казино, після — справжній титул магазину й жодної згадки чужого бренду. Усі п’ять точок входу закрито, чотири закладки видалено, очищений файл вийшов байт у байт ідентичним еталону — підтверджено двома незалежними способами. Кнопка «показати ще» на сайті працює як працювала.

04Магазин електроніки · квота хостингу 48 ГБ

Замість дорожчого тарифу — 19,8 ГБ звільненого місця.

  • Витрати
  • OpenCart

Акаунт розрісся до 35 ГБ із 48 і продовжував рости, тож напрошувався перехід на дорожчий тариф. Виявилось, що місце з’їдали не товарні фото, а мініатюри, які сам движок магазину генерував без стиснення. Я стиснув наявні й пропатчив движок, щоб нові одразу виходили нормальними. Тариф міняти не довелось, і місце не повзе назад.

Зайнято на акаунтіквота 48 ГБ
до35 ГБ
після28 ГБ
−20%
Як саме це знайдено й зроблено

Симптом

Акаунт розрісся до 35 ГБ із 48 і продовжував рости. Очевидний хід — купити більший тариф.

Заміряно

Росли не оригінали товарних фото: 18 → 19 ГБ за чотири дні. Кеш мініатюр за той самий час пішов з 8,8 до 16 ГБ.

Стокова бібліотека зображень OpenCart пише JPEG-мініатюри якістю 90 і взагалі не стискає PNG. Тобто кеш був не наслідком великої кількості товарів — його виробляв сам движок, щоразу коли в нього просили новий розмір.

Причина

Стиснути наявне означало купити кілька тижнів. Движок мусив перестати це виробляти — невелика правка бібліотеки зображень плюс один прохід по тому, що вже лежить на диску.

Результат

Оброблено 74 649 оригіналів і близько 83 000 мініатюр — з обмеженням якості, ніколи не збільшуючи файл, без зміни форматів і без дотику до бази. 794 файли перевірено на цілісність, зіпсованих нуль. Оригінали: 30 264 МБ → 18 281 МБ. Кеш мініатюр: 16 ГБ → 8,2 ГБ.

Своє

Те, що можна відкрити й помацати

Кейси вище анонімні, тож перевірити їх ви не можете — це чесна ціна за нерозголошення. Зате ці проєкти мої власні: я їх зробив і сам же тримаю.

  • nonogramhub.com

    Node · nginx · власна аналітика

    Сайт головоломок, який я веду повністю сам: генерація, контент, власна аналітика, сервер, деплой. Зручне місце, щоб побути власним замовником і дізнатися, що саме дратує у твоїх власних рішеннях.

  • MeoniQ

    NestJS · PostgreSQL · React · Tauri · Android

    Застосунок для особистих знань і медіа. Виходить як веб, десктопна програма й Android-застосунок з одного релізного процесу — з міграціями бази й автооновленнями. Таке будуєш лише тоді, коли сам це щодня експлуатуєш.

  • Universal Import

    PHP 8 · OpenCart 3 · MySQL

    Модуль імпорту для OpenCart 3 за письмовим ТЗ: майстер профілів, сухі прогони, мапінг полів і значень, знімки перед змінами. Написаний тому, що кожен готовий імпортер дублює додані руками товари на другому запуску.

Платформи

З чим працюю

  • OpenCart 3

    Більшість моєї роботи: і нові магазини, і чужі — з базою під ними та купою сторонніх модулів зверху, які роками ніхто не відкривав.

  • WooCommerce і WordPress

    Магазини на WordPress, зокрема ті, де якийсь плагін робить те, чого вже ніхто не може пояснити.

  • Laravel і React

    Коли задача переросла магазин: CRM, інструменти для ваших людей, внутрішні системи, якими користуються цілий день.

  • Ваш сервер

    Linux, nginx, Apache, бекапи, задачі за розкладом, сертифікати. Можу працювати з вашим хостингом, не потребуючи третьої людини в розмові.

Для технічного читача

PHP 8 · MySQL / MariaDB · PostgreSQL · плани запитів та індексація · OCMOD · фіди XML / YML · Rozetka · Prom · SalesDrive · Node · NestJS · TypeScript · Inertia · Astro · Tauri · pm2 · cron · GitHub Actions · розбір логів доступу · ImageMagick, jpegoptim, pngquant

З ким ви матимете справу

Найцікавіше — це півдня перед правкою.

Мене звати Вадим Степанюк, живу в Україні, працюю з клієнтами тут і за кордоном. [РОКІВ] років роблю й підтримую інтернет-магазини.

Половина моєї роботи приїжджає вже зламаною і вже чужою: магазин, зроблений п’ять років тому, обріслий тридцятьма модулями від чотирьох розробників, який віддає помилки в обід у вівторок. Код, який я зрештою пишу, рідко буває великим. Цікаве — це півдня перед ним, витрачені на те, щоб довести, хто саме з тридцяти підозрюваних винен.

Тому ж у кожному кейсі вище цифри стоять з обох боків. Правка, яку не можна заміряти, — це правка, яку ви змушені прийняти на віру. А на системі, яку будували не ви, віра коштує дорого.

Контакти

Напишіть, що потрібно.

Для початку достатньо кількох речень. Якщо це поломка — коли почалась і що змінювали приблизно тоді. Якщо новий проєкт — що за товар і чи є вже сайт. Відповідаю протягом робочого дня.

Те, що ви надсилаєте, я використовую лише щоб вам відповісти.