БАЗА:
rel="noopener noreferrer" — или как не отдать ключи от сайта чужой вкладке
Коллеги, признавайтесь: сколько раз вы писали target="_blank" без единой мысли о том, что происходит «под капотом»? Я тоже так делал. А потом разобрался — и теперь делюсь, чтобы вы не наступили на те же грабли.
Суть проблемы
Когда вы открываете ссылку через target="_blank", новая страница получает доступ к объекту window.opener вашего сайта. Через него она может выполнить:
window.opener.location = 'https://фишинг-сайт.рф'
И вуаля — пока пользователь читает «ту самую статью», его оригинальная вкладка тихо перенаправляется на фейковую страницу логина. Это называется reverse tabnabbing. Звучит как теория? В 2018–2020 годах на этом ловили реальные фишинговые кампании.
Решение — одна строка
<a href="https://example.com"
target="_blank"
rel="noopener noreferrer"
>Внешний
ресурс</a>Разберём по полочкам
• noopener — рубит доступ к window.opener. Новая вкладка становится «сиротой» и не знает, кто её открыл.
• noreferrer — прячет Referer-заголовок + автоматически включает noopener.
А зачем указывать, если браузеры и так добавляют noopener?
С 2021 года все современные браузеры (Chrome 88+, Firefox 79+, Safari) автоматически ставят noopener для target="_blank". Но!
1. Вы не контролируете, какой браузер у пользователя.
2. Код читает следующий разработчик — пусть сразу видит намерение.
3. Линтеры (ESLint vue/no-template-target-blank, react/jsx-no-target-blank) ругаются, если атрибут не указан явно.
4. Меньше шума в CI — спокойнее сон.
Когда можно обойтись без noreferrer
Если вам нужна аналитика переходов по Referer (партнёрские ссылки, метрики) — ставьте только noopener. Но для обычных внешних ссылок — полный комплект.
Чек-лист для продакшена
✅ Все <a target="_blank"> на внешние домены → rel="noopener noreferrer"
✅ Кнопки-ссылки (<button onclick="window.open(...)">) → noopener передаётся вторым аргументом: window.open(url, '_blank', 'noopener')
✅ Настроить ESLint-правило, чтобы не пропускать «голые» target="_blank"
Коллеги, одна строчка в разметке закрывает целую категорию уязвимостей. Не «авось браузер разберётся», а явная защита в коде.
Вот так. Идем кодить дальше! 🚀