Выберите продукт

TLS к upstream в Nginx: proxy_ssl_verify, SNI и клиентские сертификаты

Гайд для админов: как шифровать и проверять соединение от Nginx до бэкенда по TLS. Объясняем роль SNI, строгую проверку сертификатов (публичный или частный УЦ) и mTLS с клиентским сертификатом. Даны рабочие конфиги, ошибки и отладка.
TLS к upstream в Nginx: proxy_ssl_verify, SNI и клиентские сертификаты

Зачем шифровать трафик до 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;
}

Пример конфигураций proxy_ssl и SNI для upstream в Nginx.

Верификация сертификата 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.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

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.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Типовые паттерны конфигурации

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 могут попасть в логи.

Отладка TLS к upstream: примеры логов Nginx и команды openssl.

Безопасные значения и практические рекомендации

  • Отключите старые протоколы: используйте 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. Дальше вы сможете аккуратно внедрить пиннинг, приватный УЦ и клиентские сертификаты там, где это оправдано.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: как исправить sudo: no space left on device из-за /tmp, /var/tmp и journald OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить sudo: no space left on device из-за /tmp, /var/tmp и journald

Ошибка sudo: no space left on device в Debian и Ubuntu не всегда означает, что переполнен весь диск. Часто проблема в /tmp, /var/t ...
Debian/Ubuntu: MySQL ERROR 1045 Access denied for user — причины и исправление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: MySQL ERROR 1045 Access denied for user — причины и исправление

Ошибка MySQL ERROR 1045 Access denied for user на Debian и Ubuntu часто возникает после установки, миграции, смены пароля или обно ...
Debian/Ubuntu: Nginx upstream sent too big header при PHP — причины и исправление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: Nginx upstream sent too big header при PHP — причины и исправление

Ошибка Nginx upstream sent too big header на Debian/Ubuntu с PHP-FPM часто появляется после логина, установки плагинов или роста c ...