Загрузка...

Как перенастроить Laravel-приложение и Telegram-бота через прокси

laravel cover

Если 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 вместе с мониторингом, если бот не передаёт большие файлы и не создаёт высокую нагрузку.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *