Настройка безопасного и быстрого NGINX-прокси с многослойной защитой и ограничениями нагрузки. Он будет перенаправлять все запросы для обработки на другие Backend Nginx, приложения и сервисы.
Конфиг настроек заключает в себе следующее:
- HTTP → HTTP(S): все запросы редиректятся на HTTPS;
- Современные протоколы: HTTPS с TLS 1.2/1.3, HTTP/2 и HTTP/3/QUIC, 0-RTT для скорости;
- Безопасность соединения: HSTS, защита от clickjacking, XSS, MIME-sniff, ограничение рефереров и доступ к чувствительным API;
- Content Security Policy: контроль скриптов, стилей, медиа, форм и iframe;
- Backend-прокси: безопасная передача реального IP и схемы к внутреннему серверу;
- Ограничения: разрешены только GET/HEAD/POST, лимиты на размер запроса, таймауты, буферы;
- Защита от DoS: ограничение числа соединений и скорости запросов;
- Host-фильтр: сервер отвечает только на запросы к своему домену.
Ссылки по теме:
Будут использоваться:
- Debian: v13.1 (Trixie);
- Nginx: v1.26.3;
- Fail2Ban: 1.1.0;
Установка пакета:
# apt install nginx
Nginx:
Настройка nginx.conf:
# cp /etc/nginx/nginx.conf{,.bkp}
# nano /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
worker_cpu_affinity auto;
pid /run/nginx.pid;
#error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
# Максимальное количество соединений одного воркера;
worker_connections 1024;
# Метод выбора соединений;
use epoll;
# Принимать максимально возможное количество соединений;
multi_accept on;
}
http {
##
# Basic Settings
##
# Задаёт максимальный размер хэш-таблиц;
proxy_headers_hash_max_size 512;
# Задаёт размер корзины для хэш-таблиц;
proxy_headers_hash_bucket_size 128;
# Максимальный размер хэш-таблиц типов;
types_hash_max_size 2048;
# Метод отправки данных sendfile эффективнее чем read+write;
sendfile on;
# Накапливать заголовки и данные файла и отправлять их одним крупным TCP-пакетом;
tcp_nopush on;
# Отправлять мелкие пакеты сразу, без задержки Nagle;
tcp_nodelay on;
# Ограничивает объём данных, который может передан за один вызов sendfile(). Нужно для исключения ситуации когда одно соединение может целиком захватить воркер;
sendfile_max_chunk 1m;
# Минимальный размер для включения режима прямого чтения;
directio 4m;
# Ограничивает скорость передачи ответа клиенту;
limit_rate 12m;
# Ограничение скорости отдачи будет накладываться после;
limit_rate_after 1024m;
# Отключить вывод версии nginx в ответе;
server_tokens off;
# Сбрасывать соединение если клиент перестал читать ответ;
reset_timedout_connection on;
# Задаем зону разделяемой памяти (ip_conn);
limit_conn_zone $binary_remote_addr zone=ip_conn:1m;
# Зона разделяемой памяти (ip_req) и скорость запросов в сек;
limit_req_zone $binary_remote_addr zone=ip_req:1m rate=5r/s;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
# Разрешает указанные протоколы;
ssl_protocols TLSv1.2 TLSv1.3;
# Указывает, чтобы при использовании протоколов SSLv3 и TLS серверные шифры были более приоритетны, чем клиентские;
ssl_prefer_server_ciphers on; # add on for Intermediate;
# Наборы шифров (TLS 1.3 порядок шифров фиксирован в самом протоколе);
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
# Задаёт кривую для ECDHE-шифров;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
# Тип и объём кэша для хранения параметров сессий. Параметр shared задает общий для всех рабочих процессов nginx кэш;
ssl_session_cache shared:SSL:3m;
# Задаёт время, в течение которого клиент может повторно использовать параметры сессии;
ssl_session_timeout 12h;
# Разрешает или запрещает возобновление сессий при помощи TLS session tickets;
ssl_session_tickets off;
# Разрешает или запрещает прикрепление OCSP-ответов сервером;
#ssl_stapling on;
# Разрешает или запрещает проверку сервером ответов OCSP;
#ssl_stapling_verify on;
# Для преобразования имени хоста OCSP responder’а в адрес;
#resolver 172.16.5.57 valid=300s;
#resolver_timeout 5s;
##
# Logging Settings
##
access_log off;
#access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Настройка sites-available/xnet.icu:
Основной параметр для режима работы "Reverse Proxy" это "proxy_pass". Именно он указывает куда именно пересылать (проксировать) запросы, которые пришли от клиента. В данном случае будем пересылать запросы на другой хост с "Backend-Nginx".
# nano /etc/nginx/sites-available/xnet.icu
server {
listen 80;
server_name xnet.icu;
#return 301 https://$host$request_uri;
return 301 https://xnet.icu$request_uri;
}
server {
listen 443 ssl;
listen 443 quic reuseport;
http2 on;
http3 on;
quic_gso on; # Включить Generic Segmentation Offload для QUIC (ускоряет передачу);
quic_retry on; # Включить повторные попытки QUIC соединений при потере пакетов;
ssl_early_data on; # Включить 0-RTT соединения TLS для ускорения HTTPS;
server_name xnet.icu;
# HTTP/3 (QUIC) - Объявляем поддержку протокола;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
# Основная защита соединения;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Базовые меры безопасности;
add_header X-Frame-Options "DENY" always; # Защита от Clickjacking;
add_header X-Content-Type-Options "nosniff" always; # Защита от MIME-sniffing;
add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Минимизирует передачу Referer;
add_header X-XSS-Protection "1; mode=block" always; # (Устарело, но можно оставить для IE);
add_header X-Permitted-Cross-Domain-Policies "none" always; # Блокирует Flash / Adobe crossdomain.xml;
# Изоляция и политика разрешений;
add_header Cross-Origin-Opener-Policy "same-origin" always; # Изоляция вкладки (COOP);
add_header Cross-Origin-Embedder-Policy "require-corp" always; # Изоляция ресурсов (COEP);
add_header Cross-Origin-Resource-Policy "same-origin" always; # Разрешает загрузку ресурсов только со своего домена;
add_header Permissions-Policy "camera=(),microphone=(),geolocation=(),payment=(),usb=(),magnetometer=(),gyroscope=(),fullscreen=(self)" always; # Отключает доступ к чувствительным API;
# CSP - Content Security Policy;
add_header Content-Security-Policy "default-src 'self' xnet.icu; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' cdn.jsdelivr.net; img-src 'self'; font-src 'self'; connect-src 'self'; media-src 'self'; frame-ancestors 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" always;
ssl_certificate /etc/letsencrypt/live/xnet.icu/fullchain.pem; # path to fullchain.pem | .crt;
ssl_certificate_key /etc/letsencrypt/live/xnet.icu/privkey.pem; # path to privkey.pem | .key;
#ssl_trusted_certificate /etc/letsencrypt/live/xnet.icu/chain.pem; # Если включён ssl_stapling;
ssl_dhparam /etc/ssl/nginx/dhparam.pem; # | else <= TLS 1.3;
#access_log off;
access_log /var/log/nginx/xnet.icu_access.log combined if=$log_auth_fail;
error_log /var/log/nginx/xnet.icu_error.log; # + crit
location / {
# Клиент → nginx → backend | upload больших файлов;
proxy_request_buffering on;
# Backend → nginx → клиент | download больших файлов / стриминг;
proxy_buffering on;
proxy_pass http://local_ip:80;
proxy_set_header Host $host; # Передаёт оригинальный Host клиента (домен);
proxy_set_header X-Real-IP $remote_addr; # Передаёт реальный IP-адрес клиента;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Добавляет IP клиента в цепочку прокси;
proxy_set_header X-Forwarded-Proto $scheme; # Указывает схему (http/https), с которой пришёл запрос;
# Ограничиваем методы для Web-сервера (запрещаем всё кроме нужных);
limit_except GET HEAD POST {
deny all;
}
}
# Количество и размер буферов для чтения большого заголовка запроса клиента;
large_client_header_buffers 4 8k;
# Задание буфера для заголовка и тела запроса;
client_header_buffer_size 4k;
client_body_buffer_size 128k;
# Ограничение на размер тела запроса;
client_max_body_size 12m;
# Таймаут при чтении тела запроса клиента;
client_body_timeout 5;
# Таймаут при чтении заголовка запроса клиента;
client_header_timeout 3;
# Таймаут при передаче ответа клиенту;
send_timeout 3;
# Таймаут, по истечению которого keep-alive соединение с клиентом не будет закрыто со стороны сервера;
keepalive_timeout 10 10;
# Число соединений с одного IP-адреса;
limit_conn ip_conn 5;
# Максимальный всплеск запросов (burst);
limit_req zone=ip_req burst=10 nodelay;
# Коннекты только к своему домену;
if ($host !~ ^(xnet\.icu)$) {
return 444;
}
# Защита от пустого Host или IP-запросов;
if ($host = "") {
return 444;
}
# Количество доступных методов обращения к Web-серверу;
#if ($request_method !~ ^(GET|HEAD|POST)$) {
# return 444;
#}
}
Настройка Fail2ban + Nftables:
Будет показан пример обыной работы, а также в режиме "incremental" банов. Ограничение будет происходить по одновременным соединениям (limit_conn), частоте запросов (limit_req) и неудачным попыткам авторизации (limit-auth).
Установка пакета и настройка служб:
# apt install fail2ban
# systemctl enable --now nftables.service
# systemctl status nftables.service
# systemctl status fail2ban.service
nginx.conf
# nano /etc/nginx/nginx.conf
# Задаем зону разделяемой памяти (ip_conn);
limit_conn_zone $binary_remote_addr zone=ip_conn:1m;
# Зона разделяемой памяти (ip_req) и скорость запросов в сек;
limit_req_zone $binary_remote_addr zone=ip_req:1m rate=5r/s;
../conf.d/log_auth_fail.conf:
# nano /etc/nginx/conf.d/log_auth_fail.conf (раздел http)
# Логируем метод запроса и статусы, 401 (неверный логин/пароль) и 403 (доступ запрещён);
map ${request_method}_${status} $log_auth_fail {
default 0;
POST_401 1;
}
../sites-available/xnet.icu:
# nano /etc/nginx/sites-available/xnet.icu
# Число соединений с одного IP-адреса;
limit_conn ip_conn 5;
# Максимальный всплеск запросов (burst);
limit_req zone=ip_req burst=10 nodelay;
access_log /var/log/nginx/xnet.icu_access.log combined if=$log_auth_fail;
Настройка основного конфига F2B: fail2ban.local
Зададим время, после которого необходимо удалять баны из базы данных. Должно быть ">=" чем "bantime".
# cp /etc/fail2ban/fail2ban.{conf,local}
# cat > /etc/fail2ban/fail2ban.local << EOF
[Definition]
dbpurgeage = 3w
EOF
Настройка конфига: jail.local
# cp /etc/fail2ban/jail.{conf,local}
# cat > /etc/fail2ban/jail.local << EOF -или- (# cat << EOF > /etc/fail2ban/jail.local)
[DEFAULT]
findtime = 1d
bantime = 2w
maxretry = 1
ignoreip = 127.0.0.1 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
# banning action
banaction = nftables-multiport
banaction_allports = nftables-allports
EOF
../action.d/nftables.conf:
В файле ../action.d/nftables.conf в разделе [Init] - можно просмотреть дополнительные настройки. Например (blocktype = reject, table = f2b-table).
- Задаем действие для заблокированных IP в цепочке f2b-chain:
# nano /etc/fail2ban/action.d/nftables.conf
[Init]
blocktype = drop
Правила F2B:
# nano /etc/fail2ban/jail.d/nginx_limits.conf
[nginx-conn]
enabled = true
port = http,https
filter = nginx-limit-conn
logpath = /var/log/nginx/xnet.icu_error.log
[nginx-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/xnet.icu_error.log
[nginx-auth]
enabled = true
port = http,https
filter = nginx-limit-auth
logpath = /var/log/nginx/xnet.icu_access.log
# cp /etc/fail2ban/filter.d/nginx-limit-req.conf /etc/fail2ban/filter.d/nginx-limit-conn.conf
# nano /etc/fail2ban/filter.d/nginx-limit-conn.conf
[Definition]
ngx_limit_conn_zones = [^"]+
failregex = ^%(__prefix_line)slimiting connections by zone "(?:%(ngx_limit_conn_zones)s)", client: <HOST>,
ignoreregex =
datepattern = {^LN-BEG}
# nano /etc/fail2ban/filter.d/nginx-limit-req.conf
[Definition]
ngx_limit_req_zones = [^"]+
failregex = ^%(__prefix_line)slimiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>,
ignoreregex =
datepattern = {^LN-BEG}
# nano /etc/fail2ban/filter.d/nginx-limit-auth.conf
[Definition]
failregex = ^<HOST> -.*"POST /login .*" 401
ignoreregex =
Перезапускаем сервисы:
# systemctl restart nftables.service fail2ban.service
- Перечитать настройки:
# fail2ban-client reload
Проверка работы:
- f2b:
# fail2ban-client status
# fail2ban-client status nginx-conn
# fail2ban-client status nginx-req
# fail2ban-client status nginx-auth
# fail2ban-client set nginx-req unbanip 1.2.3.4 | banip - забанить
# fail2ban-client unban --all
- nft:
# nft list ruleset
# nft list table inet f2b-table
- Проверка Regex:
# fail2ban-regex /var/log/nginx/xnet.icu_error.log /etc/fail2ban/filter.d/nginx-limit-req.conf
# fail2ban-regex /var/log/nginx/xnet.icu_error.log /etc/fail2ban/filter.d/nginx-limit-conn.conf
# fail2ban-regex /var/log/nginx/xnet.icu_access.log /etc/fail2ban/filter.d/nginx-limit-auth.conf
Полный сброс (удалить базу):
# systemctl stop fail2ban
# rm /var/lib/fail2ban/fail2ban.sqlite3
# systemctl start fail2ban
F2B - Incremental:
Настройка основного конфига F2B: fail2ban.local
Зададим время, после которого необходимо удалять баны из базы данных. Должно быть ">=" чем "bantime".
# cp /etc/fail2ban/fail2ban.{conf,local}
# cat > /etc/fail2ban/fail2ban.local << EOF
[Definition]
dbpurgeage = 9w
EOF
Настройка конфига: jail.local
# nano /etc/fail2ban/jail.local
[DEFAULT]
findtime = 1d
bantime = 15m
maxretry = 3
ignoreip = 127.0.0.1 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
# banning action
banaction = nftables-multiport
banaction_allports = nftables-allports
# increment ban
bantime.increment = true
bantime.rndtime = 15m
bantime.maxtime = 8w
# bantime * bantime.multipliers = 15m 1h 6h 1d 1w 2w 4w 8w
bantime.multipliers = 1 4 24 96 672 1344 2688 5376
#bantime.overalljails = false
Перезапускаем службу:
# systemctl restart fail2ban.service
# nft list ruleset