Если Laravel-приложение размещено на российском хостинге, например на Beget, может возникнуть проблема с работой Telegram-бота: webhook от Telegram не доходит до приложения, а исходящие запросы к Telegram Bot API работают нестабильно или полностью блокируются.
В этой статье разберём практический пример: есть приложение my.app на Laravel с интеграцией Telegram-бота через Nutgram. Сам сайт находится на хостинге в РФ, а внешний зарубежный сервер используется как промежуточный gateway. В результате Telegram работает через зарубежный сервер, а приложение продолжает физически находиться на российском хостинге.
Задача и исходная схема
Изначально приложение работало напрямую:
Telegram → my.app/api/telegram/webhook
my.app → api.telegram.org
На практике такая схема может ломаться, если:
webhook от Telegram не доходит до приложения;
исходящие запросы к api.telegram.org блокируются;
HTTP/SOCKS-прокси работает нестабильно;
команда установки webhook ходит напрямую в Telegram и не использует proxy-настройки клиента.
В примере приложение использует .env-переменные:
TELEGRAM_WEBHOOK_URL="${APP_URL}/api/telegram/webhook"
TELEGRAM_PROXY_URL=http://user:password@proxy.example.com:23521
А в config/nutgram.php прокси применялся только на уровне Nutgram-клиента:
'config' => [
'client' => [
'proxy' => env('TELEGRAM_PROXY_URL'),
'timeout' => 30,
'connect_timeout' => 10,
],
],
Проблема в том, что бот может использовать этот прокси при отправке сообщений, но отдельные команды или кастомный код на Illuminate\Support\Facades\Http могут ходить напрямую в Telegram. Например, команда установки webhook может не использовать настройки Nutgram-клиента.
В результате часть Telegram-запросов идёт через прокси, а часть — напрямую.
Более надёжный вариант — сделать отдельный Telegram gateway на зарубежном сервере.
Новая схема:
Telegram
↓
https://telegram.example.com/app-webhook/SECRET_PATH
↓
зарубежный VPS / Nginx
↓
https://my.app/api/telegram/webhook
Для исходящих запросов:
my.app
↓
https://telegram.example.com/tg-api/botTOKEN/sendMessage
↓
зарубежный VPS / Nginx
↓
https://api.telegram.org/botTOKEN/sendMessage
Так Laravel-приложение продолжает работать на российском хостинге, но вся коммуникация с Telegram проходит через зарубежный сервер.
Подготовка VPS и домена
Для схемы понадобится:
зарубежный VPS;
домен или поддомен;
Nginx;
Certbot для HTTPS;
доступ к настройкам Laravel-приложения.
В примере используются условные значения:
VPS IP: 203.0.113.10
Gateway-домен: telegram.example.com
Приложение: https://my.app
Webhook Laravel: https://my.app/api/telegram/webhook
Создание DNS-записи
В DNS-панели нужно создать A-запись:
telegram.example.com → 203.0.113.10
Проверить DNS можно с VPS:
dig @1.1.1.1 telegram.example.com +short
dig @8.8.8.8 telegram.example.com +short
Ожидаемый результат:
203.0.113.10
Что такое SECRET_PATH
SECRET_PATH — это длинный случайный фрагмент URL, который используется как дополнительная защита webhook-адреса.
Например, вместо открытого адреса:
https://telegram.example.com/app-webhook
используется адрес:
https://telegram.example.com/app-webhook/002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4
Сгенерировать такой путь можно командой:
openssl rand -hex 32
Пример результата:
002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4
Этот путь не заменяет полноценную авторизацию, но снижает вероятность случайных запросов к webhook.
Настройка Nginx как Telegram gateway
На VPS создаём отдельный Nginx-конфиг:
nano /etc/nginx/sites-available/telegram-gateway
Пример конфига:
server {
listen 80;
listen [::]:80;
server_name telegram.example.com;
location = /health {
access_log off;
return 200 "ok\n";
}
location = /app-webhook/002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4 {
proxy_pass https://my.app/api/telegram/webhook;
proxy_ssl_server_name on;
proxy_set_header Host my.app;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_connect_timeout 15s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 20m;
}
location /tg-api/ {
access_log off;
rewrite ^/tg-api/(.*)$ /$1 break;
proxy_pass https://api.telegram.org;
proxy_ssl_server_name on;
proxy_set_header Host api.telegram.org;
proxy_connect_timeout 15s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 50m;
}
}
Активируем конфиг:
ln -sf /etc/nginx/sites-available/telegram-gateway /etc/nginx/sites-enabled/telegram-gateway
nginx -t
systemctl reload nginx
Проверяем health endpoint:
curl http://telegram.example.com/health
Ожидаемый ответ:
ok
SSL-сертификат через Certbot
Выпускаем сертификат:
certbot --nginx -d telegram.example.com
Проверяем:
curl -I https://telegram.example.com/health
Ожидаемо:
HTTP/1.1 200 OK
После этого gateway готов принимать webhook от Telegram и проксировать запросы к Telegram Bot API.
Настройка Laravel и Nutgram
Теперь нужно изменить конфигурацию приложения.
Было:
TELEGRAM_WEBHOOK_URL="${APP_URL}/api/telegram/webhook"
TELEGRAM_PROXY_URL=http://user:password@proxy.example.com:23521
Стало:
TELEGRAM_WEBHOOK_URL=https://telegram.example.com/app-webhook/002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4
TELEGRAM_API_URL=https://telegram.example.com/tg-api
TELEGRAM_PROXY_URL=
Смысл изменений:
TELEGRAM_WEBHOOK_URL — внешний webhook URL, который будет установлен в Telegram;
TELEGRAM_API_URL — новый base URL для исходящих запросов к Telegram Bot API;
TELEGRAM_PROXY_URL — старый HTTP-прокси больше не используется.
Обновление config/nutgram.php
Конфиг Nutgram лучше сделать так, чтобы API URL брался из .env:
'config' => [
'api_url' => env('TELEGRAM_API_URL', 'https://api.telegram.org'),
'client' => array_filter([
'proxy' => env('TELEGRAM_PROXY_URL') ?: null,
'timeout' => 30,
'connect_timeout' => 10,
]),
],
Если TELEGRAM_PROXY_URL пустой, параметр proxy не попадёт в конфигурацию клиента.
После изменения .env и конфигов нужно очистить кэш:
php artisan config:clear
php artisan cache:clear
Если в production используется config cache:
php artisan config:cache
Проверка исходящих запросов
Через Laravel Tinker можно проверить, какой API URL реально используется:
php artisan tinker
Внутри Tinker:
config('nutgram.config.api_url');
Ожидаемый результат:
https://telegram.example.com/tg-api
Потом можно проверить запрос к Telegram через gateway:
use Illuminate\Support\Facades\Http;
$apiUrl = rtrim(config('nutgram.config.api_url'), '/');
$token = env('TELEGRAM_BOT_TOKEN');
$url = "{$apiUrl}/bot{$token}/getMe";
Http::get($url)->json();
Если всё работает, Telegram вернёт информацию о боте:
[
"ok" => true,
"result" => [
"id" => 123456789,
"is_bot" => true,
"username" => "ExampleBot",
],
]
Если приходит:
[
"ok" => false,
"error_code" => 404,
"description" => "Not Found",
]
чаще всего это значит, что токен пустой или используется неправильная переменная окружения. Например, запрос фактически уходит на:
https://telegram.example.com/tg-api/bot/getMe
вместо:
https://telegram.example.com/tg-api/botTOKEN/getMe
В таком случае нужно проверить название переменной токена в .env и config/nutgram.php.
Установка webhook через VPS
Если команда php artisan telegram:webhook использует прямой Http-запрос к Telegram и не берёт настройки Nutgram-клиента, проще установить webhook вручную с VPS.
Сначала удалить старый webhook:
curl -X POST "https://api.telegram.org/botBOT_TOKEN/deleteWebhook"
Затем установить новый:
curl -X POST "https://api.telegram.org/botBOT_TOKEN/setWebhook" \
-d "url=https://telegram.example.com/app-webhook/002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4" \
-d "drop_pending_updates=true" \
-d "allowed_updates=[\"message\",\"callback_query\"]"
Проверить:
curl "https://api.telegram.org/botBOT_TOKEN/getWebhookInfo"
Ожидаемый результат:
{
"ok": true,
"result": {
"url": "https://telegram.example.com/app-webhook/002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4",
"has_custom_certificate": false,
"pending_update_count": 0,
"ip_address": "203.0.113.10",
"allowed_updates": [
"message",
"callback_query"
]
}
}
Теперь Telegram отправляет webhook не напрямую на российский хостинг, а на зарубежный VPS. VPS уже проксирует запрос на Laravel-приложение.
Проверка webhook
На VPS можно посмотреть входящие запросы:
tail -f /var/log/nginx/access.log
После отправки сообщения боту должен появиться POST-запрос на путь:
/app-webhook/002fe828934816d4f781c3a6a3216167249dd30762d8725d9a2e581a8a3c0ed4
Если запрос на VPS есть, но бот не отвечает, нужно смотреть Laravel-логи на хостинге:
tail -f storage/logs/laravel.log
Проверка отправки сообщений
В Tinker можно проверить отправку сообщения через gateway:
use Illuminate\Support\Facades\Http;
$apiUrl = rtrim(config('nutgram.config.api_url'), '/');
$token = env('TELEGRAM_BOT_TOKEN');
Http::post("{$apiUrl}/bot{$token}/sendMessage", [
'chat_id' => YOUR_CHAT_ID,
'text' => 'Тестовое сообщение через Telegram gateway',
])->json();
Если сообщение пришло в Telegram, исходящий канал работает.
Безопасность и мониторинг
После настройки важно не оставить gateway полностью открытым и не засветить токен.
Не публиковать Telegram Bot Token
Telegram Bot Token — это фактически пароль к управлению ботом. С его помощью можно:
отправлять сообщения от имени бота;
менять webhook;
удалять webhook;
получать информацию о боте;
ломать сценарии работы приложения.
Если токен случайно попал в чат, Git, логи, скриншот или публичную переписку, его нужно перевыпустить в BotFather:
/mybots → выбрать бота → API Token → Revoke current token
После перевыпуска нужно:
обновить токен в .env;
очистить config cache Laravel;
заново установить webhook;
проверить getWebhookInfo.
Отключить логирование /tg-api/
В Nginx-конфиге для /tg-api/ важно оставить:
access_log off;
Telegram Bot API использует токен прямо в URL:
/tg-api/botTOKEN/sendMessage
Если access log включён, токен может попасть в /var/log/nginx/access.log.
Ограничить доступ к /tg-api/
После проверки gateway лучше ограничить /tg-api/ только исходящим IP хостинга.
Пример:
location /tg-api/ {
access_log off;
allow HOSTING_OUTGOING_IP;
deny all;
rewrite ^/tg-api/(.*)$ /$1 break;
proxy_pass https://api.telegram.org;
proxy_ssl_server_name on;
proxy_set_header Host api.telegram.org;
proxy_connect_timeout 15s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 50m;
}
После изменения:
nginx -t
systemctl reload nginx
Исходящий IP хостинга можно определить по access log, временно сделав тестовый запрос с приложения на gateway.
Добавить monitoring endpoint
В Nginx-конфиге уже есть простой health endpoint:
location = /health {
access_log off;
return 200 "ok\n";
}
Его можно добавить в Uptime Kuma или другой мониторинг:
URL: https://telegram.example.com/health
Keyword: ok
Interval: 60 seconds
Также полезно мониторить:
https://my.app/health
https://telegram.example.com/health
TCP 443 на telegram.example.com
Итоговая схема
После настройки получается такая архитектура:
Telegram
↓ webhook
telegram.example.com на зарубежном VPS
↓ reverse proxy
my.app на российском хостинге
my.app
↓ Telegram API requests
telegram.example.com/tg-api
↓ reverse proxy
api.telegram.org
Такой подход решает сразу несколько проблем:
webhook от Telegram приходит на зарубежный VPS;
Laravel-приложение получает webhook через reverse proxy;
исходящие запросы к Telegram Bot API идут через gateway;
старый нестабильный HTTP-прокси больше не нужен;
бот продолжает работать, даже если прямой Telegram-трафик с хостинга блокируется.
Для небольшого Telegram-бота такой gateway можно спокойно держать на малом VPS вместе с мониторингом, если бот не передаёт большие файлы и не создаёт высокую нагрузку.