Зачем шифровать трафик до upstream и что именно настраивается
Когда Nginx выступает реверс‑прокси, у нас две независимые TLS‑границы: внешняя (клиент ↔ Nginx) и внутренняя (Nginx ↔ upstream). Внешняя защищается обычными ssl_* директивами сервера. На фронте используйте валидные сертификаты; оформить и продлить их удобно через SSL-сертификаты.
Внутренняя — набором proxy_ssl_* в блоках location/server и/или upstream. Ошибка многих — ограничиться HTTPS на фронте и возить трафик к бэкенду в чистом HTTP. Это ломает модель «ноль доверия», мешает сегментации, увеличивает риск перехвата в датацентре или между зонами.
Цель — обеспечить:
- Шифрование: TLS 1.2/1.3, современные шифры.
- Верификацию сервера:
proxy_ssl_verify onс доверенным УЦ или пиннингом. - SNI: корректное имя в ClientHello, чтобы upstream отдал нужный сертификат.
- При необходимости mTLS: Nginx предъявляет клиентский сертификат upstream‑у.
- Производительность: reuse сессий и keepalive для снижения накладных расходов.
Ключевые директивы proxy_ssl в Nginx
Базовые элементы:
proxy_ssl_server_name on— включает SNI на соединении к upstream.proxy_ssl_name <name|$var>— задаёт имя для SNI, если нужно переопределить.proxy_ssl_verify on— включает проверку сертификата upstream.proxy_ssl_trusted_certificate— путь к файлу с доверенными корнями/цепочкой для проверки upstream.proxy_ssl_verify_depth N— глубина проверки цепочки сертификатов.proxy_ssl_protocols TLSv1.2 TLSv1.3— версии TLS к upstream.proxy_ssl_ciphers ...— шифросьюты для TLS<1.3. Для TLS 1.3 действует своя логика выбора.proxy_ssl_certificate,proxy_ssl_certificate_key— клиентский сертификат и ключ для mTLS.proxy_ssl_password_file— файл с паролем для зашифрованного ключа.proxy_ssl_crl— список отзыва (CRL), если вы пользуетесь собственным УЦ с CRL.proxy_ssl_session_reuse on— повторное использование TLS‑сессий к upstream.
Важно: ssl_* и proxy_ssl_* — разные плоскости конфигурации. Настройки фронта никак автоматически не переносятся на соединение к upstream.
SNI: когда нужен и как не промахнуться
SNI — имя сервера, которое клиент (здесь Nginx) указывает при рукопожатии TLS, чтобы upstream выбрал корректный сертификат из набора. Если вы проксируете на HTTPS по IP или по домену, не совпадающему с CN/SAN в сертификате, и SNI не задан, проверка сертификата скорее всего провалится.
Правила практики:
- Всегда включайте
proxy_ssl_server_name on, еслиproxy_passуказывает на доменное имя. - Если
proxy_passуказывает на IP, задайтеproxy_ssl_nameравным DNS‑имени, на которое выписан сертификат upstream, и синхронно выставьтеHostзаголовок. - Не путайте SNI и HTTP Host: первое — на уровне TLS, второе — на уровне HTTP. Для некоторых бэкендов важны оба.
# Минимум для корректного SNI при доменном upstream
location /api/ {
proxy_pass https://api.example.com;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-bundle-upstream.pem;
proxy_ssl_verify_depth 3;
proxy_set_header Host api.example.com;
}
Если upstream в конфиге задан по IP, а сертификат выписан на имя:
location /api/ {
proxy_pass https://203.0.113.10:443;
proxy_ssl_server_name on;
proxy_ssl_name api.example.com; # SNI переопределяем вручную
proxy_set_header Host api.example.com; # HTTP Host тоже приводим к имени
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-bundle-upstream.pem;
}

Верификация сертификата upstream: системный УЦ, частный УЦ, пиннинг
Выгрузить трафик «на честном слове» — это оставить proxy_ssl_verify off. Так делать не стоит. Верификация не только подтверждает подлинность, но и защищает от MITM на участке между сегментами.
1) Доверие системным корневым УЦ
Если сертификат upstream выписан публичным УЦ, используйте системный bundle. Пути отличаются по дистрибутивам, проверьте актуальные:
- Debian/Ubuntu:
/etc/ssl/certs/ca-certificates.crt - RHEL/CentOS/Alma/Rocky:
/etc/pki/tls/certs/ca-bundle.crt
location /ext/ {
proxy_pass https://external.example.com;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_verify_depth 3;
}
2) Частный УЦ или self-signed
Положите цепочку УЦ, которая выдала серверный сертификат upstream, в отдельный PEM‑файл и укажите его в proxy_ssl_trusted_certificate. Верификация пройдёт только при совпадении SNI/имени и корректной цепочке.
location /internal/ {
proxy_pass https://api.internal.lan;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca-chain.pem;
proxy_ssl_verify_depth 2;
proxy_ssl_crl /etc/nginx/trust/internal-ca.crl; # опционально, если ведёте CRL
}
3) Пиннинг конкретного сертификата
Строгий вариант: в proxy_ssl_trusted_certificate кладём непосредственно сертификат upstream (leaf), превращая его в якорь доверия. После ротации серверного сертификата файл нужно обновить и перезагрузить Nginx.
Пиннинг повышает безопасность, но усложняет ротации. Для критичных сервисов стоит автоматизировать обновление файла доверия вместе с выпуском нового сертификата upstream.
mTLS к upstream: когда и как предъявлять клиентский сертификат
Если upstream настроен требовать клиентский сертификат, Nginx должен предъявить свой. Это часто используется для внутренних API, биллинга, платёжных шлюзов, сервисов с повышенными требованиями.
location /secure-api/ {
proxy_pass https://secure.api.internal;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca-chain.pem;
# mTLS: сертификат и приватный ключ клиента (Nginx)
proxy_ssl_certificate /etc/nginx/tls/client.crt;
proxy_ssl_certificate_key /etc/nginx/tls/client.key;
# Если ключ зашифрован
# proxy_ssl_password_file /etc/nginx/tls/client.pass;
}
Рекомендации по безопасности ключа:
- Права 0400/0440, владелец — пользователь, под которым работает Nginx (обычно
nginxилиwww-data). - Ключ и сертификат хранить отдельно от публичных конфигов и бэкапов.
- Организовать плановую ротацию и безопасную доставку ключевого материала.
Производительность: keepalive и reuse TLS‑сессий
Каждое новое TLS‑рукопожатие стоит RTT и CPU. Снижайте накладные расходы:
- Включите keepalive на уровне группы upstream.
- Оставьте
proxy_ssl_session_reuse on(по умолчанию включено). - Не забывайте
proxy_http_version 1.1и корректныйConnection.
upstream backend_pool {
server svc1.internal:443 resolve;
server svc2.internal:443 resolve;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name app.example.com;
location / {
proxy_pass https://backend_pool;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_ssl_server_name on;
# Все узлы используют один и тот же сертификат на имя service.internal
proxy_ssl_name service.internal;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca-chain.pem;
}
}
Здесь SNI фиксирован на общий SAN/CN сертификата, который установлен на всех узлах. Это самый удобный паттерн для балансировки по нескольким HTTPS‑бэкендам.
DNS и динамика адресов: не забудьте resolver
Если вы используете имена в proxy_pass или в upstream server ... resolve;, Nginx должен знать, каким резолвером пользоваться. Иначе имя разрешится только при старте, а изменение DNS не подтянется.
resolver 1.1.1.1 9.9.9.9 valid=300s ipv6=off;
resolver_timeout 5s;
upstream api_pool {
server api-a.internal:443 resolve;
server api-b.internal:443 resolve;
keepalive 64;
}
valid= задаёт TTL кеша DNS в Nginx. В продакшене выбирайте умеренные значения, чтобы не терять реакцию на смену адресов. Если вы разворачиваете Nginx на отдельном сервере, удобно начать с управляемого VDS. Для выбора панели администрирования посмотрите материал Сравнение панелей для VDS.
Типовые паттерны конфигурации
1) Внешний API с публичным сертификатом
location /ext/ {
proxy_pass https://api.vendor.com;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_verify_depth 3;
proxy_set_header Host api.vendor.com;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
2) Внутренний сервис с приватным УЦ
location /internal/ {
proxy_pass https://svc.internal.lan;
proxy_ssl_server_name on;
proxy_ssl_name svc.internal.lan;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/org-root-ca.pem;
proxy_ssl_crl /etc/nginx/trust/org-root-ca.crl;
}
3) Проксирование по IP с переопределением SNI и Host
location /ip-route/ {
proxy_pass https://192.0.2.50:443;
proxy_ssl_server_name on;
proxy_ssl_name api.internal.example;
proxy_set_header Host api.internal.example;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca.pem;
}
4) mTLS для критичного бэкенда
location /payments/ {
proxy_pass https://payments.internal;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca-chain.pem;
proxy_ssl_certificate /etc/nginx/tls/payments-client.crt;
proxy_ssl_certificate_key /etc/nginx/tls/payments-client.key;
# proxy_ssl_password_file /etc/nginx/tls/payments-client.pass;
}
Частые ошибки и отладка
- certificate verify failed: неверный
proxy_ssl_trusted_certificate, отсутствует промежуточный УЦ или глубинаproxy_ssl_verify_depthслишком мала. Проверьте цепочку upstream. - name mismatch: SNI/Host не совпадает с CN/SAN серверного сертификата. Задайте
proxy_ssl_nameиHostявно. - host not found in upstream: забыли
resolverили DNS недоступен. - no required SSL certificate was sent: upstream требует клиентский сертификат, но не задан
proxy_ssl_certificate. - handshake failure: несовместимые версии TLS/шифры. Уточните
proxy_ssl_protocolsиproxy_ssl_ciphers.
Минимальный набор команд для диагностики сертификата upstream (выполняется на хосте с Nginx):
# Посмотреть цепочку и имя в сертификате upstream
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts < /dev/null
# Коротко о сертификате (CN, SAN, срок)
openssl x509 -noout -subject -issuer -dates -ext subjectAltName -in server-cert.pem
Для более подробной диагностики временно повысьте уровень логирования:
error_log /var/log/nginx/error.log info;
# или
error_log /var/log/nginx/error.log debug;
Не оставляйте уровень debug в продакшене надолго: лог раздуется, а чувствительные детали TLS могут попасть в логи.

Безопасные значения и практические рекомендации
- Отключите старые протоколы: используйте
proxy_ssl_protocols TLSv1.2 TLSv1.3. - Для TLS 1.2 задайте строгий набор шифров, исключите
RSA key exchangeи слабые алгоритмы. Пример:HIGH:!aNULL:!MD5:!3DES:!SHA1:!RSA. - Следите за сроком действия доверенных файлов. Для пиннинга — особенно.
- Храните ключи для mTLS с минимальными правами, изолируйте их от бэкапов.
- Автоматизируйте ротацию: перегенерация trust‑файлов и
nginx -t && nginx -s reload.
Про аспект фронтовой безопасности (301, HSTS, выпуск SSL) читайте: Перенос домена с 301, HSTS и SSL.
Несколько продвинутых приёмов
Динамический выбор SNI через map
Когда один и тот же location ходит в разные домены (канареечные релизы, мульти‑регион), удобно маппить имя для SNI:
map $cookie_region $upstream_sni {
default service.internal;
eu service.eu.internal;
us service.us.internal;
}
location / {
proxy_pass https://backend_pool;
proxy_ssl_server_name on;
proxy_ssl_name $upstream_sni;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca-chain.pem;
}
WebSocket поверх HTTPS к upstream
Если бэкенд говорит WebSocket, TLS‑часть настраивается так же, как и для HTTPS; добавьте апгрейд заголовков:
location /ws/ {
proxy_pass https://ws.internal;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/trust/internal-ca-chain.pem;
}
Ограничение наборов доверия по сервисам
Не кладите «всё и сразу» в один глобальный trust‑файл. Разделяйте доверенные УЦ по сервисам: взлом одного trust‑пути не позволит подменить другой upstream.
Контроль качества и эксплуатация
- Периодически проверяйте ошибки в
error_logпо ключевым словам: verify, handshake, certificate. - Собирайте метрики по количеству установленных соединений к upstream, времени рукопожатий и проценту провалов.
- Тестируйте ротации сертификатов на стенде: сначала меняйте trust‑файлы, потом серверные сертификаты, затем делайте контролируемый reload.
Итоги
Грамотная настройка TLS к upstream в Nginx — это три кита: корректный SNI, строгая верификация сертификата и, при необходимости, mTLS. Добавьте к этому DNS‑резолвинг с учётом динамики, keepalive и reuse TLS‑сессий — и получите безопасный и производительный контур между прокси и бэкендом. Начните с простого: включите proxy_ssl_verify и proxy_ssl_server_name, укажите верный proxy_ssl_trusted_certificate. Дальше вы сможете аккуратно внедрить пиннинг, приватный УЦ и клиентские сертификаты там, где это оправдано.


