Короткий ответ — надёжного «универсального» способа через только клиент‑скрипт нет: telegram-web-app.js может быть подключён на любой странице и не даёт 100% гарантии, а поля initData / platform могут быть пустыми. Надёжно определить, что вы запущены именно внутри Telegram Web App, можно только комбинацией мер — и лучше всего заранее передавать явный флаг из бота (рекомендую).
Что сделать (практическое руководство)
1) Самое надёжное (рекомендуемый способ)
- Когда бот формирует кнопку/ссылку на ваш фронтенд, добавляйте в URL явный параметр, например:
https://example.com/?tg_webapp=1
- На фронтенде проверяйте этот параметр. Это кросс‑платформенно и надежно (даже на iOS).
Пример:
const urlParams = new URLSearchParams(window.location.search);
const fromTelegramParam = urlParams.get('tg_webapp') === '1';
if (fromTelegramParam) {
// работа внутри Telegram Web App
}
2) Надёжная «вторичная» проверка — комбинированная эвристика
Если вы не можете изменить URL на стороне бота, используйте несколько признаков вместе (чтобы сгладить особенности iOS/Android):
- наличие объекта window.Telegram && window.Telegram.WebApp
- непустое window.Telegram.WebApp.initData (или initDataUnsafe)
- window.Telegram.WebApp.platform !== 'unknown'
- document.referrer содержит 't.me' / 'telegram.me' / 'web.telegram.org'
- navigator.userAgent содержит 'Telegram' (часто бывает в UA встроенного браузера)
Пример функции, объединяющей эвристики:
function isProbablyTelegramWebApp() {
const params = new URLSearchParams(window.location.search);
if (params.get('tg_webapp') === '1' || params.get('from') === 'telegram_webapp') return true;
const hasWebApp = !!(window.Telegram && window.Telegram.WebApp);
if (!hasWebApp) return false;
const initData = window.Telegram.WebApp.initData || '';
const platform = window.Telegram.WebApp.platform || '';
const ua = navigator.userAgent || '';
const ref = document.referrer || '';
if (initData) return true;
if (platform && platform !== 'unknown') return true;
if (/telegram/i.test(ua)) return true;
if (/t\.me|telegram\.me|web\.telegram\.org/.test(ref)) return true;
return false;
}
Замечания по надёжности:
- UA и referrer можно подделать / они могут отсутствовать (особенно на iOS).
- telegram-web-app.js сам по себе может быть загружен на странице в обычном браузере — просто наличие window.Telegram.WebApp не гарантирует, что вы внутри telegram‑контейнера.
- initData — идеальный маркер, но он может быть пустым, если веб‑приложение не было открыто через веб‑контейнер telegram (например, открыто напрямую в браузере) или из‑за особенностей клиента.
3) Безопасность и серверная валидация (для платежей)
Если вы полагаетесь на initData для аутентификации/платежей — проверяйте initData подпись на сервере согласно официальной документации Telegram (похожая валидация как у виджета Telegram Login). Никогда не доверяйте только клиентской проверке при совершении критичных операций.
Почему лучше править бота (еще раз)
- Если вы контролируете бота — добавление простого GET‑параметра в URL (или использования web_app опции в KeyboardButton) — самый простой и кросс‑платформенный способ. Это решает проблему раз и навсегда и избавляет от ненадёжных эвристик, особенно на iOS, где поведение встроенного браузера может быть специфичным.
Если нужно — могу:
- подсказать, как добавить параметр в кнопку бота (пример для Bot API / python‑telegram‑bot),
- или помочь доработать функцию детекции под ваш Vue + vue‑tg стек и кейс с iOS.