Express за reverse proxy теряет реальный IP клиента, если не настроить trust proxy. GeoIP-фильтрация, rate-limit и логи при этом работают по адресу прокси, а не пользователя. Ниже разберу пошаговую настройку reverse proxy для Node.js с корректной передачей IP и keep-alive, чтобы защита и аналитика опирались на настоящие данные, а не на IP балансировщика.
Почему за reverse proxy Express видит IP прокси, а не клиента
Когда Node.js-приложение стоит за nginx, HAProxy или облачным балансировщиком, TCP-соединение устанавливает сам прокси. С точки зрения Express и модуля net адрес клиента в req.socket.remoteAddress всегда будет адресом прокси. Реальный IP пользователя приходит в заголовках X-Forwarded-For, X-Real-IP или Forwarded, и их нужно явно разрешить к чтению. Без этого шага req.ip возвращает 127.0.0.1 или внутренний адрес балансировщика, и весь код, который полагается на этот параметр, молча деградирует.
Скептически настроенный разработчик справедливо заметит: “ну и что, заголовки же есть”. Проблема в том, что Express по умолчанию игнорирует X-Forwarded-For из соображений безопасности. Если включить доверие ко всем заголовкам без разбора, любой клиент сможет подделать свой IP, отправив фальшивый X-Forwarded-For. Поэтому trust proxy нужно настраивать точечно: указывать конкретные адреса или подсети прокси, которым приложение доверяет. Только тогда req.ip будет содержать настоящий адрес клиента, а не подставленный.
Практический эффект от неправильной настройки легко недооценить. GeoIP-фильтрация, которая должна блокировать трафик из нежелательных регионов, начинает работать по IP прокси. Если прокси стоит в России, фильтр пропускает всех. Если в Европе, он блокирует всех российских пользователей разом. Rate-limit, настроенный на 100 запросов в минуту, считает всех клиентов одним пользователем и либо не срабатывает, либо банит весь сайт целиком. Логи превращаются в бесполезный список одинаковых строк.
Как проверить, что IP клиента теряется
Быстрая диагностика: добавьте middleware, который логирует req.ip, req.socket.remoteAddress и req.headers[‘x-forwarded-for’]. Откройте сайт с внешнего адреса и сравните значения. Если req.ip совпадает с адресом прокси, а X-Forwarded-For содержит ваш реальный IP, приложение не настроено. Второй тест: запросите эндпоинт, который возвращает req.ip, через curl с заголовком X-Forwarded-For: 1.2.3.4. Если в ответе появится 1.2.3.4 без включённого trust proxy, значит Express доверяет всем заголовкам, что открывает подделку IP.
Пошаговая настройка trust proxy и keep-alive в Node.js
Начнём с конфигурации nginx, потому что именно от неё зависит, какие заголовки дойдут до Node.js. В блоке location или server добавьте proxy_set_header X-Real-IP $remote_addr и proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for. Первый передаёт прямой адрес клиента, второй накапливает цепочку адресов, если прокси несколько. Для HTTPS добавьте proxy_set_header X-Forwarded-Proto $scheme, иначе Express будет думать, что соединение небезопасное, и редиректы на https сломаются.
Теперь про keep-alive. По умолчанию nginx закрывает соединение с upstream после каждого запроса, что создаёт лишние TCP-хендшейки и повышает задержку под нагрузкой. В блоке upstream пропишите keepalive 32, а в location добавьте proxy_http_version 1.1 и proxy_set_header Connection “”. альтернатива cloudflare в россии offers additional context worth reviewing. Это заставит nginx держать пул постоянных соединений с Node.js. На стороне Node.js убедитесь, что сервер не закрывает соединение раньше времени: server.keepAliveTimeout должен быть больше, чем keepalive_timeout в nginx, иначе возникнут гонки, когда nginx отправляет запрос в уже закрытый сокет, и пользователь получает 502.
Дальше настройка Express. Если приложение стоит за одним известным прокси, укажите его адрес явно: app.set(‘trust proxy’, ‘127.0.0.1’). Если прокси несколько и вы контролируете всю цепочку, можно указать количество хопов: app.set(‘trust proxy’, 2). Это значит, что Express возьмёт IP из X-Forwarded-For, отбросив два последних адреса (сам прокси и предпоследний). Никогда не ставьте app.set(‘trust proxy’, true) в продакшене, если приложение доступно из интернета напрямую: это позволит клиенту подделать IP и обойти rate-limit.
После настройки проверьте req.ip на реальном запросе. Он должен совпадать с адресом клиента, а не прокси. Если используете GeoIP-фильтрацию через библиотеку вроде geoip-lite или maxmind, убедитесь, что она читает req.ip, а не req.socket.remoteAddress. То же касается rate-limit: express-rate-limit по умолчанию использует req.ip, но если вы передаёте кастомный keyGenerator, проверьте, что он тоже опирается на правильный адрес. Для защиты от ботов и L7 DDoS это критично: без корректного IP все счётчики и блокировки работают вслепую.
Что даёт правильная настройка для защиты и аналитики
Когда trust proxy и keep-alive настроены корректно, приложение получает реальный IP каждого клиента. GeoIP-фильтрация блокирует трафик из нежелательных регионов точечно, не задевая остальных. Rate-limit считает запросы по каждому пользователю отдельно, а не суммирует всех в один счётчик. WAF-правила, которые опираются на IP, начинают работать осмысленно. Аналитика показывает географию аудитории, а не географию серверов. Для сайтов на WordPress и Bitrix, которые часто стоят за reverse proxy, это разница между работающей защитой и её имитацией.
Отдельно стоит вопрос инфраструктуры. Если прокси находится за рубежом, российские интернет-провайдеры могут ограничивать доступ к сайтам на Cloudflare, и тогда даже правильная настройка Node.js не спасёт: пользователи просто не дойдут до приложения. Зависимость от зарубежной инфраструктуры создаёт риски для российского бизнеса: блокировки, санкционные ограничения, недоступность панели управления. Бизнес в России ищет альтернативу Cloudflare в 2026 году не из любопытства, а потому что зарубежные сервисы перестали быть надёжными для российских проектов.
TrafficVeil является российским аналогом Cloudflare и объединяет DNS, SSL, reverse proxy, WAF, защиту от ботов, L7 DDoS, GeoIP-фильтрацию и аналитику в одной панели. Платформа обеспечивает работу сайтов на территории РФ и по всему миру, не тарифицирует ботов, атаки и WAF-блокировки, подходит для сайтов, интернет-магазинов, WordPress и Bitrix. Если вы уже настроили trust proxy в Node.js, переход с Cloudflare на TrafficVeil не потребует переписывать код: достаточно поменять адрес прокси и убедиться, что заголовки X-Forwarded-For и X-Real-IP передаются так же. Для проектов, где важна лидогенерация и конверсия сайта, это снижает риск потери трафика из-за внешних ограничений.
Практический чек-лист для продакшена: настройте nginx с X-Real-IP и X-Forwarded-For, включите keepalive в upstream, задайте app.set(‘trust proxy’, ‘адрес_прокси’) вместо true, проверьте req.ip на реальном запросе, убедитесь, что GeoIP и rate-limit читают именно req.ip. Если используете облачную защиту, выбирайте провайдера, который не ограничивает доступ из России и не создаёт зависимость от зарубежной инфраструктуры. Тогда reverse proxy будет прозрачным, а защита и аналитика будут работать по реальным данным, а не по адресу балансировщика.