Улучшена функция получения IP-адреса клиента и добавлен тестовый эндпоинт

- Расширена логика получения IP-адреса с учетом новых заголовков, таких как CF-Connecting-IP и Forwarded.
- Добавлено детальное логирование заголовков для упрощения отладки.
- Внедрен тестовый эндпоинт для проверки извлечения IP-адресов и отображения заголовков запроса.
This commit is contained in:
Fovway 2025-10-26 15:20:04 +07:00
parent ceed63249e
commit 0cb0b27f20
2 changed files with 222 additions and 12 deletions

149
NGINX_PROXY_SETUP.md Normal file
View File

@ -0,0 +1,149 @@
# Настройка nginx Proxy Manager для корректной передачи IP-адресов
## Проблема
При использовании nginx Proxy Manager в режиме bridge, реальный IP-адрес клиента может не передаваться корректно, и вместо него всегда показывается IP-адрес прокси-сервера (например, 90.189.198.107).
## Решение
### 1. Настройка nginx Proxy Manager
В настройках прокси-хоста в nginx Proxy Manager добавьте следующие заголовки:
#### В разделе "Advanced" → "Custom Nginx Configuration":
```nginx
# Передача реального IP-адреса клиента
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Дополнительные заголовки для лучшей совместимости
proxy_set_header X-Client-IP $remote_addr;
proxy_set_header X-Forwarded $scheme://$host;
# Установка доверенного прокси
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Server $host;
```
#### Или в разделе "Custom Nginx Configuration" (если доступен):
```nginx
location / {
proxy_pass http://your-app-container:3000;
# Передача реального IP-адреса
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Client-IP $remote_addr;
# Дополнительные настройки
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Server $host;
# Таймауты
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Буферизация
proxy_buffering off;
proxy_request_buffering off;
}
```
### 2. Настройка Docker Compose (если используется)
Если ваш nginx Proxy Manager работает в Docker, убедитесь, что в docker-compose.yml правильно настроена сеть:
```yaml
version: "3.8"
services:
nginx-proxy-manager:
image: jc21/nginx-proxy-manager:latest
container_name: nginx-proxy-manager
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- npm-network
restart: unless-stopped
your-app:
image: your-app-image
container_name: your-app
networks:
- npm-network
restart: unless-stopped
networks:
npm-network:
driver: bridge
```
### 3. Проверка работы
После настройки используйте тестовый эндпоинт для проверки:
```bash
curl -H "X-Forwarded-For: 192.168.1.100" https://your-domain.com/api/debug/ip
```
Или откройте в браузере: `https://your-domain.com/api/debug/ip`
### 4. Ожидаемый результат
В ответе должно быть:
```json
{
"detected_ip": "192.168.1.100", // Реальный IP клиента
"headers": {
"x-forwarded-for": "192.168.1.100",
"x-real-ip": "192.168.1.100",
"x-client-ip": "192.168.1.100"
}
}
```
### 5. Отладка
Если IP-адрес все еще неправильный:
1. Проверьте логи nginx Proxy Manager
2. Убедитесь, что заголовки передаются (используйте `/api/debug/ip`)
3. Проверьте настройки сети Docker
4. Убедитесь, что nginx Proxy Manager не находится за дополнительными прокси
### 6. Дополнительные настройки
#### Для Cloudflare
Если вы используете Cloudflare, добавьте:
```nginx
proxy_set_header CF-Connecting-IP $http_cf_connecting_ip;
```
#### Для AWS Load Balancer
```nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
```
## Примечания
- После изменения настроек nginx Proxy Manager перезапустите контейнер
- Убедитесь, что ваш Node.js сервер настроен с `app.set("trust proxy", true)`
- Тестовый эндпоинт `/api/debug/ip` можно удалить после настройки

View File

@ -304,29 +304,67 @@ function getClientIP(req) {
console.log("x-forwarded-for:", req.headers["x-forwarded-for"]);
console.log("x-real-ip:", req.headers["x-real-ip"]);
console.log("x-client-ip:", req.headers["x-client-ip"]);
console.log("cf-connecting-ip:", req.headers["cf-connecting-ip"]); // Cloudflare
console.log("x-forwarded:", req.headers["x-forwarded"]);
console.log("forwarded:", req.headers["forwarded"]);
console.log("req.connection.remoteAddress:", req.connection?.remoteAddress);
console.log("req.socket.remoteAddress:", req.socket?.remoteAddress);
console.log("ALL HEADERS:", JSON.stringify(req.headers, null, 2));
console.log("========================");
// Используем req.socket.remoteAddress напрямую, так как nginx передает внутренний IP контейнера
let ip = req.socket?.remoteAddress;
// Приоритет заголовков для nginx proxy manager:
// 1. X-Real-IP (часто используется nginx)
// 2. X-Forwarded-For (стандартный заголовок)
// 3. CF-Connecting-IP (если используется Cloudflare)
// 4. X-Client-IP
// 5. req.ip (Express с trust proxy)
// 6. req.socket.remoteAddress (прямое соединение)
// Если remoteAddress недоступен, пробуем заголовки
if (!ip || ip === "::1") {
ip =
req.headers["x-forwarded-for"]?.split(",")[0]?.trim() ||
req.headers["x-real-ip"] ||
req.headers["x-client-ip"] ||
req.ip ||
req.connection?.remoteAddress ||
"unknown";
let ip = null;
// Проверяем X-Real-IP (nginx часто использует этот заголовок)
if (req.headers["x-real-ip"]) {
ip = req.headers["x-real-ip"];
}
// Проверяем X-Forwarded-For (берем первый IP из списка)
else if (req.headers["x-forwarded-for"]) {
ip = req.headers["x-forwarded-for"].split(",")[0].trim();
}
// Проверяем CF-Connecting-IP (Cloudflare)
else if (req.headers["cf-connecting-ip"]) {
ip = req.headers["cf-connecting-ip"];
}
// Проверяем X-Client-IP
else if (req.headers["x-client-ip"]) {
ip = req.headers["x-client-ip"];
}
// Проверяем Forwarded заголовок (RFC 7239)
else if (req.headers["forwarded"]) {
const forwarded = req.headers["forwarded"];
const forMatch = forwarded.match(/for=([^;,\s]+)/);
if (forMatch) {
ip = forMatch[1];
}
}
// Используем req.ip (Express с trust proxy)
else if (req.ip && req.ip !== "::1" && req.ip !== "127.0.0.1") {
ip = req.ip;
}
// В крайнем случае используем socket remoteAddress
else if (
req.socket?.remoteAddress &&
req.socket.remoteAddress !== "::1" &&
req.socket.remoteAddress !== "127.0.0.1"
) {
ip = req.socket.remoteAddress;
}
// Очищаем IP от скобок IPv6 и портов
// Очищаем IP от скобок IPv6, портов и кавычек
if (ip && ip !== "unknown") {
// Убираем скобки IPv6
ip = ip.replace(/[[\]]/g, "");
// Убираем кавычки если есть
ip = ip.replace(/['"]/g, "");
// Обрабатываем IPv6 адреса
if (ip.startsWith("::ffff:")) {
@ -710,6 +748,29 @@ app.get("/api/user", requireApiAuth, (req, res) => {
});
});
// Тестовый эндпоинт для проверки IP-адресов (только для отладки)
app.get("/api/debug/ip", (req, res) => {
const clientIP = getClientIP(req);
res.json({
detected_ip: clientIP,
headers: {
"x-forwarded-for": req.headers["x-forwarded-for"],
"x-real-ip": req.headers["x-real-ip"],
"x-client-ip": req.headers["x-client-ip"],
"cf-connecting-ip": req.headers["cf-connecting-ip"],
"x-forwarded": req.headers["x-forwarded"],
forwarded: req.headers["forwarded"],
},
express: {
req_ip: req.ip,
socket_remoteAddress: req.socket?.remoteAddress,
connection_remoteAddress: req.connection?.remoteAddress,
},
all_headers: req.headers,
});
});
// Страница с заметками (требует аутентификации)
app.get("/notes", requireAuth, (req, res) => {
// Получаем цвет пользователя для предотвращения FOUC