↩️ Назад

Категории

Обновление Joomla 5.4 → 6.1.4 и ускорение мобильной версии: разбор одного дня

13.10.2026 | из категории: Joomla

Обновление Joomla 5.4 → 6.1.4 и ускорение мобильной версии

Один рабочий день, два реальных сайта — копия и боевой. Обновление ядра до мажорной версии, починка старого компонента, приведение cookie-баннера в порядок и снижение веса страниц в десятки раз. Ниже — что именно делалось, какие ловушки встретились и что это дало.

47 → 87оценка PageSpeed для мобильных (лабораторные данные), до и после
12,86 → 0,83 МБвес картинок на странице врачей
1,72 → 0,51 МБвес картинок на главной
34 из 34страниц сверены «до/после» без расхождений

01Условия задачи

Медицинский сайт на Joomla, общий хостинг, никакой оболочки: доступ только по FTP, командной строки нет. Обновлять ядро при этом нужно — версия 5.4.5 устарела, а переход на 6 требует подготовленной почвы.

Что усложняло работу:

  • нет SSH. Функции exec, shell_exec, system и passthru закрыты хостингом. Обходной путь, который спасает: proc_open остаётся доступным — через него из PHP-скрипта запускается консоль Joomla.
  • версия PHP задаётся у каждого домена своя и только в панели хостинга. Пять вариантов правил-обработчиков в .htaccess хостинг проигнорировал: поднять PHP «из файлов» не получилось. План обновления нужно строить после замера версии у конкретного домена.
  • в шаблонной части два активных шаблона — одна часть страниц собирается одним, другая другим. Правки и новый код приходилось дублировать, иначе половина сайта оставалась старой.
  • боевой и копия — разные установки с разными базами. Учётка FTP заходит то в корень копии, то в корень боевого (владелец переключал её трижды), поэтому первое действие в каждой сессии — прочитать configuration.php и понять, где ты находишься.
  • старый компонент врачей линии «для Joomla 3» (сборка 2017 года) — главный кандидат на поломку.

02Переход 5.4 → 6: как это устроено

На мажорную версию можно перейти только с «мостового» выпуска — то есть с x.4. С сайта ниже 5.4 обновление до 6 даже не предложат.

Канал обновлений и ловушка «уже последняя версия»

В 5.4 канал обновлений больше не меняет адрес сервера: адрес один, а канал передаётся меткой ветки при запросе (для «следующей» — номер мажорной версии плюс один).

# поставить канал «следующая ветка»
php cli/joomla.php core:update:channel next

# и обязательно ОПРОСИТЬ сервер обновлений
php cli/joomla.php core:update:check -v
грабля

Если переключить канал и сразу запустить обновление, команда отвечает «You already have the latest Joomla! version. 5.4.9» и ничего не делает: очередь обновлений в базе взята со старого канала. Лечится не повтором обновления, а одним опросом core:update:check — после него в ответе появляется «New Joomla Version 6.1.4 is available».

Предусловия: ядро само не пустит

Перед мажорным обновлением Joomla проверяет условия и останавливается, если они не выполнены — при этом файлы не тронуты. Требуется, чтобы старый плагин совместимости был выключен, а новый включён. Сообщение выглядит так:

[ERROR] The 'Behaviour - Backward Compatibility' plugin is enabled.
[INFO]  The pre-conditions for a major upgrade are not fulfilled.

Две детали, которые стоят времени, если о них не знать. Первая: в базе может лежать несколько записей одного плагина совместимости — следы прошлых обновлений; проверка смотрит, есть ли хоть одна включённая, поэтому выключать надо все. Вторая: вместе со старым плагином приходится выключать системный плагин старого компонента — без него не запускается даже консоль Joomla.

цена решения

Пока старый плагин совместимости выключен, сайт на 5.4 отдаёт ошибку 500 на всех страницах — это не поломка правки, а ожидаемое состояние окна обновления. Предупреждать владельца нужно заранее; окно получилось коротким — около двух-трёх минут.

Два способа выполнить обновление

На копии шли консолью, на боевом владелец нажал кнопку в админке — оба пути рабочие, и это хорошая новость: вход в админку при выключенном плагине совместимости работает, и обновление из панели проходит целиком, включая правку схемы базы.

# консольный путь: запуск в фоне, журнал в файл
cd /корень/сайта
setsid nohup /opt/php/8.5/bin/php cli/joomla.php core:update -n \
  > /tmp/update.log 2>&1 < /dev/null &

Из PHP это запускается через proc_open, а журнал читается тем же скриптом-пробником. Ещё одна мелочь: консоль нужно запускать той же сборкой PHP, что и сайт — на хостинге рядом со штатной сборкой лежат версионные, и «просто php» бывает старее веба.

03После обновления: обязательные шаги

Обновление файлов — половина дела. Дальше по списку:

ШагЗачем
У плагина-замены включить подстановку старых имён
classes_aliases = 1
В поставке шестёрки она выключена. Пока не включить — старые расширения не работают.
Вернуть системный плагин старого компонента Он выключался на время окна, иначе консоль не стартовала.
cli/joomla.php maintenance:database --fix -n Дочистить структуру базы. Важно: без ключа --fix команда только перечисляет проблемы и ничего не меняет.
cli/joomla.php cache:clean -n Иначе страницы ещё 15 минут отдают старую разметку — и кажется, что ничего не сработало.

Бывает, что обновление «падает» на самом последнем шаге, при финализации: не совпадают старые и новые классы. Пугаться не нужно — файлы шестёрки уже на месте, версию видно в administrator/manifests/files/joomla.xml, а база дочищается командой выше.

04Что сломалось после обновления

Самое частое после мажорного обновления — не «Joomla сломала расширение», а удалённый метод драйвера базы. В шестёрке у MySQL-драйвера больше нет castAsChar() (у PostgreSQL и PDO он остался), и старый компонент на этом падает: страница врачей отдавала 500.

// было
$c_id = $query->castAsChar('c.id');

// стало — обычный SQL, работает на обеих версиях
$c_id = 'CAST(c.id AS CHAR)';

Правильный путь, конечно, — обновить само расширение до линейки, знающей новую ветку. Правка одной строки — это способ починить быстро и обратимо, а не решение на годы.

вторая ловушка того же компонента

Пока в его папке лежит setup/index.php, компонент пишет «is currently offline for maintenance» и свой код не выполняет. Это признак незавершённой установки, а не поломка сайта.

Заодно выяснилось, что файл языкового пакета вводит в заблуждение: он показывал версию 4.4 при ядре 5.4. Надёжнее определять версию ядра по признакам в файлах (наличие папки плагина совместимости шестёрки) — иначе легко построить неверный план обновления.

05Cookie-баннер и аналитика: пятно по 152-ФЗ

Отдельная работа того же дня. Владелец сообщил: «нажимаю „Принять все“ — ничего не происходит». При проверке выяснилось три вещи.

  • Выбор согласия хранился только в памяти браузера. Там, где она запрещена или подчищена (приватное окно, встроенный браузер мессенджера), согласие не сохранялось — баннер вылезал заново, и со стороны это выглядело как неработающая кнопка.
  • На страницах второго шаблона баннера не было вообще. При этом счётчики аналитики на них грузились без всякого согласия.
  • Вне сценария согласия работал ещё один контейнер Google Tag Manager, которого в разметке нет — его подтягивает кто-то из внешних виджетов.

Что сделано:

  • согласие теперь пишется и в память браузера, и в обычную cookie; читается из любого доступного места;
  • обработчики навешены прямо на кнопки (плюс запасной путь по клику внутри баннера), а скрытие продублировано принудительным правилом — чтобы кнопка работала независимо от сторонних стилей;
  • баннер и логика согласия добавлены во второй шаблон;
  • вся аналитика (Метрика, Calltouch, GTM) теперь запускается только после согласия.
проверено в браузере

До нажатия «Принять все» — ноль обращений к серверам аналитики. После нажатия — восемь запросов, и согласие записано в двух местах.

06Картинки: −96% веса без потери вида

Вторая часть дня — скорость. Диагностика показала, что тормозит не код сайта, а картинки: библиотека фотографий врачей лежала в формате PNG — формате без потерь, придуманном для чертежей и логотипов. Для фотографии он раздувает файл в десятки раз.

Фото врача, один и тот же размер в пикселях:
PNG (как было)1493 КБ
JPEG q8580 КБ
WebP q8232 КБ

Качество проверялось не «на слово»: собранная картинка-сравнение «оригинал | JPEG | WebP» и просмотр в масштабе 1:1 — по лицу, волосам и тексту на баннере. Разницы для посетителя нет.

Устройство кэша компонента — то, что определяет аккуратность работы

Фотографии врачей хранит сам компонент: путь к фото карточки лежит в JSON-поле media таблицы материалов, а сами файлы — в папке кэша, по шесть размеров на каждое фото. Важная деталь: файла без суффикса размера на диске нет, хотя путь в базе выглядит именно так — имя с размером компонент подставляет сам.

media/tz_portfolio_plus/article/cache/
  belkina-13_xs.webp   _s   _m   _l   _o   _xl
  # базы belkina-13.webp на диске нет — путь виртуальный
грабля, которая чуть не стоила дорого

Замена расширения в базе коснулась 47 карточек, а заранее в новый формат были переведены только те 19 наборов, что были в PNG. Пятнадцать фотографий на несколько минут стали отдавать «не найдено», пока не перевёл остальные. Вывод: сначала файлы — потом пути, и проверять отдачу каждой ссылки, а не факт заливки.

Итог по библиотеке:

ЧтоБылоСтало
19 наборов фотографий в PNG (6 размеров каждый)102,6 МБ1,5 МБ
313 файлов, лежавших в JPEG26,3 МБ4,5 МБ
Страница врачей целиком12,86 МБ0,83 МБ
Главная страница1,72 МБ0,51 МБ

Оригиналы никуда не удалялись — откат сводится к замене расширения обратно в базе.

07Почему «не оптимизировано для мобильных»

Отчёт PageSpeed выглядел как приговор, но после разбора оказалось: собственный код сайта почти не виноват. Основной вес давали чужие виджеты, подключённые к странице.

КтоСкриптыПроцессор
Встроенное видео Rutube660 КБ996 мс
Викторина Marquiz (всплывает через минуту)650 КБ424 мс
Три счётчика Яндекс.Метрики272 КБ467 мс
Ещё один контейнер Google Tag Manager118 КБ133 мс
Значки-рейтинги и чат62 КБ—

Главное открытие: iframe грузится, даже если его не видно

Викторина была свёрнута в невидимое окно и должна была всплыть через 60 секунд — но её iframe со всем кодом загружался сразу при каждом заходе. Видео — та же история. Решение: настоящий адрес переносится в data-атрибут, а в самом iframe остаётся пустышка; подстановка происходит в момент показа.

<!-- было: грузится всегда -->
<iframe class="quiz-iframe" src="https://mrqz.me/…"></iframe>

<!-- стало: адрес подставляется при показе -->
<iframe class="quiz-iframe" src="about:blank" data-dn-src="https://mrqz.me/…"></iframe>
внимание при копировании приёма

В коде показа условие должно сравнивать текущий адрес кадра с адресом из атрибута, а не с пустышкой: сравнение с «пустым» адресом в части случаев не срабатывает и окно открывается пустым. Проверка — браузером: дождаться показа и убедиться, что адрес кадра стал настоящим.

Что ещё сделано по скорости

  • Отложенная загрузка для видео, карточек и чужих значков; скрипт виджета отзывов помечен как необязательный для первой отрисовки.
  • Свои скрипты переведены в режим defer — шесть файлов больше не блокируют отрисовку страницы. Перед этим проверено, что ни один встроенный скрипт не вызывает библиотеку на этапе разбора (иначе получите «$ is not defined»): defer сохраняет порядок и выполняется до DOMContentLoaded, поэтому обработчики не ломаются.
  • Два второстепенных файла стилей перестали блокировать отрисовку (аккордеон и лайтбокс — то, что не влияет на первый экран; базовые стили трогать нельзя, иначе поедут метрики сдвига макета).
  • Кэширование статики включено правилом в .htaccess.
граница, о которой стоит знать

Правило из .htaccess подействовало не на всех: у картинок (/images/, /img/, /assets/img/, /media/) заголовки кэширования появились, а .css, .js и .svg веб-сервер отдаёт мимо Apache — на них правило не действует. Проверять нужно каждый путь отдельным запросом, а для остального писать в поддержку хостинга.

Отдельно про TTFB

В «полевых» данных время ответа сервера выглядело пугающе — 1,5 секунды. Прямой замер показал 0,13 секунды на ответ и 0,28–0,31 с на сборку страницы внутри. То есть сервер быстрый, а цифра в отчёте — это мобильная сеть посетителя плюс усреднение за 28 дней, в которое попал и период до оптимизаций.

на будущее

Полевые показатели Google обновляются медленно: сутки-двое на сдвиг и до четырёх недель на полное «выезжание» старых значений. Об этом стоит сказать владельцу заранее — иначе он будет смотреть на старые цифры и считать, что ничего не изменилось.

08Что осталось и что не вышло

Не вышло:

  • поднять версию PHP из файлов — хостинг игнорирует правила-обработчики, только панель;
  • включить кэширование для CSS и скриптов через .htaccess — их отдаёт nginx напрямую;
  • воспроизвести жалобу на cookie-баннер один в один: в чистом браузере кнопка работала во всех сценариях, включая «сначала только обязательные, потом передумал». Похоже, дело было в несохранении выбора — но точную причину у конкретного браузера подтвердить не удалось.

Осталось на решение владельца:

  • три счётчика Метрики свести к одному — это ещё около 130 КБ и 230 мс на каждом заходе;
  • кэш страниц Joomla выключен; включение ускорит повторные заходы, но требует проверки отправки формы (в кэшированной странице может оказаться устаревший ключ защиты);
  • выяснить, кто подтягивает лишний контейнер Google Tag Manager;
  • видео можно перевести в режим «обложка + запуск по клику» — тогда плеер не грузится вовсе;
  • языковой пакет не обновлялся около двух лет — часть подписей в админке английская.

09Чек-лист, если делать у себя

  • Выяснить, куда реально смотрит доступ по FTP, и прочитать configuration.php — прежде чем что-то менять.
  • Снять снимок «до»: адрес, код ответа, размер, заголовок — по всем ключевым страницам.
  • Проверить версию PHP у конкретного домена и требования целевой версии.
  • Оценить расширения по тегу совместимости; заранее предположить, что сломается.
  • Доставить последнюю заплатку текущей ветки, затем переключить канал и опросить сервер обновлений.
  • Помнить про предусловия: старый плагин совместимости — выключить (все записи), новый — включить; системный плагин старого компонента выключить на время окна.
  • Обновиться, затем: включить подстановку старых имён, вернуть плагин компонента, maintenance:database --fix, очистить кэш.
  • Сверить все страницы со снимком «до» и проверить, что расширения работают.
  • Аналитику и внешние виджеты — под согласие и под отложенную загрузку.
  • Картинки: сначала файлы в новом формате, затем пути в базе, затем чистка кэша CMS и проверка отдачи по каждой ссылке.

Материал подготовлен по итогам работ 13.10.2026 на копии и боевом сайте.

Все числа — из фактических замеров: вес страниц по HTTP, ответы сервера по журналам и замерам, оценка скорости — по лабораторным данным Google PageSpeed Insights для мобильных устройств.




Категории:

Категории

← Назад к списку

Посетителей сегодня: 0
о блоге | карта блога | 📡 Подписаться на RSS

© Digital Specialist | Не являемся сотрудниками Google, Яндекса и NASA