
Внешние ссылки сами по себе не являются проблемой для сайта. Они естественны для веба: мы ссылаемся на официальную документацию, сервисы, источники и полезные материалы. Проверять нужно не «все внешние ссылки подряд», а те, которые появились без вашего ведома, ведут на устаревшие или сомнительные домены, остались от старой рекламы либо были встроены в шаблон, модуль или другой код.
В старых инструкциях для такой проверки часто предлагали Xenu Link и поиск всех вхождений base64_decode. Для Joomla 5 и 6 такой подход уже неудобен и может привести к ошибкам. Ни Xenu, ни другие сторонние программы здесь не обязательны: достаточно phpMyAdmin, консоли браузера и файлового менеджера хостинга.
Что именно стоит искать
Проверка особенно полезна в нескольких ситуациях:
- после установки или обновления шаблона, модуля, плагина или компонента;
- после восстановления сайта или устранения заражения;
- если в Search Console, антивирусе или исходном коде появились незнакомые домены;
- если на сайте раньше использовались рекламные и партнёрские блоки;
- если нужно провести ревизию старого контента.
Главная задача — понять где хранится ссылка и выводится ли она на сайте сейчас. Один и тот же URL может находиться в материале Joomla, в HTML-модуле, в файле расширения или всего лишь в кеше.
Поиск внешних ссылок в материалах через phpMyAdmin
Основной текст материалов Joomla хранится в таблице #__content, в полях introtext и fulltext. Поэтому массовую проверку можно выполнить непосредственно в базе данных.
Для начала можно вывести первое абсолютное внешнее URL из каждого материала:
SELECT
id,
title,
REGEXP_SUBSTR(
CONCAT_WS(' ', introtext, `fulltext`),
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://[^"''<>[:space:]]+'
) AS external_url
FROM PREFIX_content
WHERE CONCAT_WS(' ', introtext, `fulltext`)
REGEXP 'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://[^"''<>[:space:]]+'
ORDER BY id; Такой список показывает, в каких материалах вообще присутствуют абсолютные ссылки. Но для аудита удобнее сгруппировать их по доменам и посмотреть, сколько раз каждый домен встречается и в скольких материалах.
SQL-запрос для списка уникальных внешних доменов
В двух местах замените example\\.com на собственный домен. Точки в имени домена нужно экранировать как \\..
WITH RECURSIVE
src AS (
SELECT
id,
title,
CONCAT_WS(' ', introtext, `fulltext`) AS rest
FROM PREFIX_content
WHERE CONCAT_WS(' ', introtext, `fulltext`)
REGEXP 'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
),
links (id, title, url, rest, n) AS (
SELECT
id,
title,
REGEXP_SUBSTR(
rest,
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
),
SUBSTRING(
rest,
REGEXP_INSTR(
rest,
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
) + CHAR_LENGTH(
REGEXP_SUBSTR(
rest,
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
)
)
),
1
FROM src
UNION ALL
SELECT
id,
title,
REGEXP_SUBSTR(
rest,
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
),
SUBSTRING(
rest,
REGEXP_INSTR(
rest,
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
) + CHAR_LENGTH(
REGEXP_SUBSTR(
rest,
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
)
)
),
n + 1
FROM links
WHERE n < 100
AND rest REGEXP
'href[[:space:]]*=[[:space:]]*["'']\\Khttps?://(?!(?:[a-z0-9-]+\\.)*example\\.com(?:[/:?#]|$))[^"''<>[:space:]]+'
)
SELECT
LOWER(REGEXP_SUBSTR(url, 'https?://\\K[^/:?#]+')) AS domain,
COUNT(*) AS links_count,
COUNT(DISTINCT id) AS articles_count
FROM links
WHERE url <> ''
GROUP BY domain
ORDER BY articles_count DESC, links_count DESC, domain; 
На реальном сайте такой запрос может вернуть десятки или сотни доменов. Это не означает, что все они вредоносные. Сначала смотрите на контекст: официальные сайты Joomla, Google, сервисы, о которых рассказывает статья, и старые партнёрские площадки могут быть вполне объяснимыми. Подозрение вызывают прежде всего неизвестные домены, которые повторяются в несвязанных материалах или которые вы точно не добавляли.
Проверяем HTML-модули Joomla
Внешняя ссылка может находиться не в материале, а в пользовательском HTML-модуле. Для таблицы модулей полезно одновременно вывести URL и состояние публикации.
SELECT
id,
title,
position,
published,
REGEXP_SUBSTR(
content,
'https?://[^"''<>[:space:]]+'
) AS external_url
FROM PREFIX_modules
WHERE content REGEXP 'https?://'
AND content NOT REGEXP 'https?://([a-z0-9-]+\\.)*example\\.com'
ORDER BY id; 
Обратите внимание на поле published: значение 0 означает, что модуль отключён. Наличие URL в таком модуле ещё не означает, что ссылка реально появляется на страницах сайта. Старые рекламные, партнёрские и подписные модули могут годами оставаться в базе в выключенном состоянии.
Как увидеть только те ссылки, которые реально выводятся в браузере
База данных показывает, где URL хранится. Но чтобы понять, присутствует ли ссылка на конкретной открытой странице, удобнее проверить DOM через консоль браузера.
Откройте нужную страницу сайта и запустите инструменты разработчика одним из удобных способов:
- F12 — открыть DevTools, затем перейти на вкладку Console;
- Ctrl + Shift + J — сразу открыть Console в Chrome, Edge или Opera;
- Ctrl + Shift + I — открыть DevTools, затем выбрать вкладку Console;
- нажать правой кнопкой мыши по странице и выбрать «Просмотреть код» / «Исследовать элемент» (название пункта зависит от браузера), после чего перейти на вкладку Console.
После открытия Console вставьте следующий код и нажмите Enter:
console.table(
[...document.querySelectorAll('a[href]')]
.map(a => {
const u = new URL(a.href, location.href);
return {
text: a.textContent.trim().replace(/\s+/g, ' ').slice(0, 80),
url: u.href,
domain: u.hostname,
rel: a.rel || ''
};
})
.filter(x =>
/^https?:/.test(x.url) &&
x.domain !== location.hostname
)
); В таблице будут видны текст ссылки, полный URL, домен и значение rel. Если результат пустой, значит на открытой странице нет внешних кликабельных ссылок <a href>.

Такой способ особенно полезен для проверки конкретной проблемной страницы: URL может храниться в базе или кеше, но не выводиться посетителю.
Поиск конкретного домена в файлах Joomla через ISPmanager
Если вы нашли подозрительный домен и хотите понять, встроен ли он в шаблон или расширение, искать лучше сам домен, а не общие функции вроде base64_decode.
В ISPmanager откройте Менеджер файлов → Поиск файлов, укажите корень сайта или конкретную папку, включите:
- Искать в подкаталогах;
- Искать по содержимому;
- в поле «Маска содержимого» — нужный домен, например
example.com.

Если поиск по всему сайту находит домен только в каталогах cache или administrator/cache, это ещё не доказательство внедрения в PHP-код. Кеш может содержать сохранённую копию материала или страницы, где такая ссылка была обычной частью текста.

Чтобы найти первоисточник, дополнительно проверьте каталоги /templates, /components, /modules и /plugins. Если там совпадений нет, а домен находится только в кеше, сначала ищите его в материалах и модулях базы данных.
Почему нельзя удалять всё, где встречается base64_decode
base64_decode() — обычная функция PHP для декодирования Base64. Она может использоваться и нормальным кодом Joomla или расширений. Поэтому сам факт её присутствия не доказывает заражение.
Опасный подход — найти все вхождения base64_decode и удалить их. Так можно повредить рабочий компонент, авторизацию, передачу параметров или другую штатную функцию.
Правильнее действовать наоборот:
- сначала определить подозрительный домен или конкретный фрагмент кода;
- понять, из какого материала, модуля, расширения или файла он выводится;
- сравнить файл с чистой версией расширения;
- и только после подтверждения удалить чужую вставку.
Как безопасно удалить нежелательную ссылку
Способ зависит от источника:
- Материал Joomla — удалите или исправьте ссылку в редакторе материала.
- HTML-модуль — отредактируйте или удалите модуль через админ-панель.
- Настройки расширения — сначала проверьте параметры компонента, плагина или модуля.
- Файл шаблона или расширения — сделайте резервную копию и сравните файл с чистым оригиналом. Если код неизвестного происхождения, безопаснее заменить расширение его официальной чистой версией, чем вырезать фрагменты наугад.
Как относиться к обычным внешним ссылкам
Удалять все внешние ссылки ради SEO не нужно. Google рекомендует ссылаться на полезные внешние источники, когда это помогает читателю. Обычная редакционная ссылка не требует nofollow. Для рекламы и платных размещений предпочтителен rel="sponsored", для пользовательских ссылок — ugc, а nofollow применяют, когда вы не хотите связывать свой сайт с ресурсом.
Подробнее: рекомендации Google по исходящим ссылкам.
Итоги
Для проверки внешних и скрытых ссылок в Joomla 5 и 6 необязательно устанавливать отдельные программы. Практичная последовательность выглядит так:
- найти внешние URL и домены в материалах через phpMyAdmin;
- проверить HTML-модули и их состояние публикации;
- на конкретной странице посмотреть реально выведенные ссылки через Console;
- если домен подозрительный — найти его по содержимому файлов в ISPmanager;
- отделить кеш от реального исходного кода;
- удалять только подтверждённую нежелательную вставку и затем повторить проверку.
Такой подход помогает не только найти действительно лишнюю ссылку, но и не повредить сайт случайным удалением нормального кода.
Переменные ($returnUri, $serialized_data, $post['back'…) выглядят подозрительно. Я бы проверил их все редактором Notepad++. Для этого выберите файл, в котором они находятся, и поочередно введите название каждой в строке поиска.
Закодированные ссылки в base64 имеют вид в коде "крякозябр", т. е. что-то наподобие вот этого:
PGRpdiBpZD0idnQtbW4iPjxhIGhyZWY9Imh0dHA6Ly
9qb29tbGEtbWFzdGVyLm9yZy8iIHRhcmdldD0iX2JsYW5rIiB0aXR
sZT0i0LvRg9GH0YjQuNC1INGI0LDQsdC70L7QvdGLING
B0LDQudGC0L7QsiDQtNC70Y8gSm9vbWxhIj7Qu9GD0YfRiNC40LUg0Y
jQsNCx0LvQvtC90Ysg0YHQsNC50YLQvtCyINC00Lv
RjyBKb29tbGE8L2E+PGJyPjxhIGhyZWY9Imh0dHA6Ly9iYXR0bGVmaW
VsZDQuY29tLnVhLyIgdGFyZ2V0PSJfYmxhbmsiIHRp
dGxlPSLQvtCx0LfQvtGAINC40LPRgNGLIEJhdHRsZWZpZWxkIDQiPtC+0LHQt
9C+0YAg0LjQs9GA0YsgQmF0dGxlZmllbGQgNDwvYT48L2Rpdj4=
Если код окажется подозрительным, скопируйте его и проверьте на сервисе base64.ru.
Если обнаружите, что это вредоносный код, удалите его полностью, начиная с самого начала.
У меня после поиска вышло очень много всего,неужели надо чисть все?)
$redirect = base64_decode($returnUri);
if(function_exists('base64_encode') && function_exists('base64_decode'))
if(function_exists('base64_encode') && function_exists('base64_decode'))
$serialized_data = base64_decode($serialized_data);
$ciphertext = base64_decode($ciphertext);
$this->setRedirect(base64_decode($post['back']));
$this->setRedirect(base64_decode($post['back']));
$return = base64_decode($return);
$type = json_decode(base64_decode($type));
$explodeArray = explode(':', base64_decode($fileName));
$override = base64_decode($app->input->get('folder'));
if (base64_decode(urldecode($file)) == 'index.php')
$location = base64_decode($app->input->get('address'));
$location = base64_decode($app->input->get('address'));