bg-tutorials

Как настроить SSL-сертификат в HAProxy

HAProxy — это TCP- и HTTP-балансировщик нагрузки, который завершает TLS-соединение, то есть расшифровывает трафик на границе сети и передаёт обычный HTTP на серверы бэкенда. Это делает его единственным звеном в цепочке, которому требуется сертификат, причём в особом формате: один файл, содержащий сертификат, все промежуточные сертификаты и приватный ключ.

Это руководство охватывает весь путь: создание CSR, сборку PEM-файла, написание frontend и backend, проверку конфигурации и перезагрузку без разрыва активных соединений. Команды были выполнены на HAProxy 3.4 — текущей ветке с долгосрочной поддержкой, выпущенной в июне 2026 года и поддерживаемой до второго квартала 2031 года.

Чего ожидает HAProxy: единый PEM-файл

Большинство серверов принимают сертификат, цепочку и ключ как три отдельных параметра. HAProxy принимает один. В его руководстве по конфигурации ключевое слово crt описывается как обозначающее «PEM-файл, содержащий как необходимые сертификаты, так и связанные с ними приватные ключи», созданный путём объединения PEM-файлов, и добавляется, что «если ваш CA требует промежуточный сертификат, его также можно объединить в этот файл».

Прежде чем начать, стоит знать о двух особенностях поведения — это сэкономит вам время в дальнейшем:

  • Ключ может находиться рядом с сертификатом, а не внутри него. Если в файле нет приватного ключа, HAProxy ищет файл с тем же путём, но с добавлением .key. Таким образом, mydomain.pem плюс mydomain.pem.key работают так же, как и один объединённый файл.
  • Вы можете указать crt на директорию. HAProxy загружает каждый найденный там файл и выбирает нужный сертификат для каждого запроса с помощью SNI. Именно так можно обслуживать несколько сайтов с одного frontend без отдельной строки bind для каждого.

Создание CSR и приватного ключа

CSR — это закодированный запрос, который вы передаёте удостоверяющему центру. Создайте его на машине HAProxy или в любом месте, где вы можете обеспечить безопасность приватного ключа, поскольку ключ никогда не покидает вашу сторону.

openssl req -new -newkey rsa:2048 -nodes 
  -keyout mydomain.key -out mydomain.csr 
  -subj "/C=US/ST=California/L=San Jose/O=Your Company/CN=mydomain.com" 
  -addext "subjectAltName=DNS:mydomain.com,DNS:www.mydomain.com"

Строка -addext на практике не является опциональной. Браузеры давно перестали сопоставлять имена хостов с Common Name и читают только Subject Alternative Name, поэтому CSR, содержащий только CN, создаёт сертификат, который не проходит проверку ни в одном современном браузере. Перечислите все имена, которые должен покрывать сертификат, включая голый домен и форму с www, если вы обслуживаете оба варианта.

Опустите параметры -subj и -addext, если предпочитаете, чтобы система запрашивала каждое поле отдельно. В любом случае вы получите два файла: mydomain.csr для отправки и mydomain.key для хранения. Убедитесь, что CSR содержит именно то, что вы ожидаете, прежде чем отправлять его — с помощью нашего декодера CSR или локально:

openssl req -noout -text -verify -in mydomain.csr

Проверьте, что записи SAN перечислены и подпись проверяется корректно. Если вы предпочитаете вообще не использовать командную строку, наш генератор CSR создаёт ту же пару прямо в браузере. Дополнительную информацию о базовых командах вы найдёте в нашем руководстве по командам OpenSSL.

Получение выпущенного сертификата

Отправьте CSR в CA, выберите тип сертификата, соответствующий тому, что вы защищаете, и завершите проверку. Сертификат для одного домена покрывает одно имя хоста, wildcard-сертификат покрывает все поддомены первого уровня, а мультидоменный (SAN) сертификат покрывает список несвязанных имён. За балансировщиком нагрузки мультидоменный вариант — распространённый выбор, поскольку один экземпляр HAProxy обычно обслуживает сразу несколько сайтов.

CA возвращает архив, содержащий ваш сертификат и промежуточную цепочку, обычно в виде файла CA bundle. Оба файла уже в формате PEM, что вам и нужно. Позаботьтесь о продлении заранее, а не потом: начиная с 15 марта 2026 года публично доверенный TLS-сертификат может действовать не более 200 дней, снижаясь до 100 дней в марте 2027 года и до 47 дней в марте 2029 года, так что ручная замена перестанет быть практичной довольно скоро.

Сборка PEM-файла, который будет читать HAProxy

Создайте директорию для сертификатов и соберите файл прямо там. С самого начала держите всё в одном месте; разбиение этого процесса между домашней директорией, /etc/haproxy и /etc/ssl — верный способ редактировать один файл, в то время как HAProxy читает совсем другой.

sudo mkdir -p /etc/haproxy/certs
sudo chmod 700 /etc/haproxy/certs

Объедините сертификат, затем промежуточные сертификаты, затем приватный ключ. Перенаправление должно выполняться от имени root, поэтому используйте конвейер с tee, а не запись sudo cat ... > /etc/haproxy/certs/..., которая завершится ошибкой доступа, потому что shell открывает выходной файл от имени вашего собственного пользователя ещё до того, как выполнится sudo:

cat mydomain.crt intermediate.crt mydomain.key 
  | sudo tee /etc/haproxy/certs/mydomain.pem > /dev/null

Теперь этот файл содержит ваш приватный ключ в открытом виде, поэтому ограничьте к нему доступ, прежде чем продолжать:

sudo chown root:root /etc/haproxy/certs/mydomain.pem
sudo chmod 600 /etc/haproxy/certs/mydomain.pem

Права доступа «только для root» здесь верны и не являются препятствием. HAProxy запускается с правами суперпользователя, что, как отмечается в его руководстве, необходимо для последующего переключения на собственного непривилегированного пользователя, и именно во время этого запуска он читает сертификат. Расширять права доступа, чтобы пользователь haproxy мог читать ключ, не нужно, и делать этого не следует.

Если вы создали CSR на другой машине, сначала скопируйте файлы, а затем удалите копии из промежуточной директории:

scp mydomain.crt intermediate.crt mydomain.key sysadmin@haproxy-server:/home/sysadmin/

Настройка HAProxy

Откройте файл /etc/haproxy/haproxy.cfg в терминальном редакторе прямо на сервере, например nano или vim. Редактируйте файл на месте, а не на рабочей станции, чтобы вы никогда не перезагружали файл, отличающийся от того, который вы протестировали.

Frontend

Один frontend может принимать как обычный HTTP, так и HTTPS. Привяжите порт 80 для перенаправления, привяжите порт 443 с сертификатом и направьте всё остальное на backend:

frontend web_frontend
    mode http
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/mydomain.pem alpn h2,http/1.1

    http-request redirect scheme https code 301 unless { ssl_fc }

    default_backend web_servers

alpn h2,http/1.1 предлагает HTTP/2 и переключается на HTTP/1.1 при необходимости. Перенаправление срабатывает только тогда, когда запрос поступил не по TLS, что и проверяет ssl_fc, поэтому запросы на порту 443 проходят напрямую.

Разместите все параметры TLS в одной строке bind

Именно здесь конфигурации HAProxy чаще всего дают сбой, причём тихо. В руководствах часто представляют усиление TLS как второй шаг, показывая новую строку bind для порта 443 с дополнительными параметрами. Если вы добавите эту строку вместо редактирования существующей, у вас окажется две строки bind для одного и того же порта, и HAProxy не станет жаловаться. Он запустится и откроет два отдельных прослушивающих сокета на порту 443 с разными настройками TLS. То, на какой из сокетов попадёт данное соединение, вы не контролируете, поэтому ваше усиление безопасности покрывает лишь примерно половину трафика.

Проверка конфигурации тоже это не отловит, и её собственная документация объясняет почему: -c «выполняет только проверку файлов конфигурации и завершается до попытки привязки«. Дублирующийся слушатель — это условие, возникающее на этапе привязки, поэтому синтаксическая проверка никогда его не увидит.

Вместо этого задайте глобальные настройки по умолчанию, чтобы каждая строка bind в файле наследовала их, и дублировать было бы нечего:

global
    ssl-default-bind-options ssl-min-ver TLSv1.2 prefer-client-ciphers
    ssl-default-bind-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
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256

Стоит знать, прежде чем копировать этот блок куда-либо: ssl-min-ver уже по умолчанию равен TLSv1.2, как сказано в собственной документации HAProxy. Установка этого значения в TLSv1.2 ничего не меняет и лишь фиксирует намерение, что вполне нормально, но это не то улучшение безопасности, каким его часто представляют. Реальную работу выполняют два параметра шифров, и они разделены намеренно: ssl-default-bind-ciphers применяется к TLS 1.2 и ниже, ssl-default-bind-ciphersuites применяется к TLS 1.3. Задайте только первый параметр — и ваши наборы шифров TLS 1.3 останутся на значениях по умолчанию.

Backend

Присвойте каждому серверу реальный адрес. Это важнее, чем кажется на первый взгляд:

backend web_servers
    mode http
    balance roundrobin
    option httpchk GET /
    server web1 10.0.0.11:80 check
    server web2 10.0.0.12:80 check

Строка сервера, записанная как server web1 :80 check, с пропущенным адресом, не вызывает ошибки. HAProxy принимает её и разрешает отсутствующий адрес в 0.0.0.0, которое его руководство трактует как специальное значение, означающее, что соединение перенаправляется на тот же IP-адрес, к которому подключился клиент. Этот адрес — сам HAProxy, поэтому backend незаметно указывает на собственный порт 80 HAProxy, который является тем самым frontend, который вы только что настроили. Трафик закольцовывается обратно в перенаправление вместо того, чтобы достичь какого-либо приложения, а проверки состояния выглядят успешными, потому что там действительно кто-то слушает. Если балансировщик отвечает на каждый HTTPS-запрос перенаправлением на самого себя, в первую очередь проверьте строки server.

Поскольку HAProxy завершает TLS, серверы backend получают обычный HTTP на порту 80 и не нуждаются в собственном сертификате. Если политика требует шифрования и на этом участке, добавьте ssl verify required и файл CA в строки server и укажите порт 443 вместо этого.

Передавайте исходную схему на backend. Поскольку backend теперь получает обычный HTTP, приложение, генерирующее абсолютные URL — как это делают WordPress, Django и Rails — будет генерировать их с http://, что проявится как смешанный контент или как петля перенаправления, создаваемая самим приложением. Добавьте одну строку во frontend:

http-request set-header X-Forwarded-Proto https if { ssl_fc }

и одну в backend, которая также передаёт реальный IP-адрес клиента:

option forwardfor

Большинству фреймворков затем нужно сказать, чтобы они доверяли этим заголовкам; эта часть настраивается в приложении, а не в HAProxy.

Проверьте конфигурацию перед применением

Никогда не перезапускайте балансировщик нагрузки с непроверенным файлом. Сначала выполните проверку:

sudo haproxy -c -V -f /etc/haproxy/haproxy.cfg

С флагом -V при успешной проверке выводится Configuration file is valid и возвращается нулевой код завершения. Без этого флага успех проходит без вывода сообщений. Любые предупреждения выводятся независимо от того, действителен файл или нет, поэтому читайте вывод, а не полагайтесь на отсутствие красного текста.

Перезагрузка вместо перезапуска

sudo systemctl reload haproxy

Различие между этими операциями реально важно на балансировщике нагрузки. Перезагрузка запускает новый процесс и сигнализирует старому «завершить то, что он делает, и уйти», так что уже начатые запросы завершаются нормально. Перезапуск сигнализирует старому процессу «немедленно завершиться, не заканчивая то, что он делал», что обрывает активные соединения, включая загрузки файлов и длительные вызовы API. Используйте перезапуск только тогда, когда перезагрузка не может подхватить изменение, например после изменения настроек процесса в глобальном разделе.

Убедитесь, что служба вернулась в рабочее состояние и слушает оба порта:

sudo systemctl status haproxy
sudo ss -tlnp | grep haproxy

Здесь вам нужен ровно один прослушивающий сокет на порт. Два сокета на порту 443 означают, что у вас дублирующаяся строка bind, описанная выше.

Проверьте, что сертификат действительно предоставляется

Проверьте, что именно предоставляет HAProxy, включая цепочку, прямо с самого сервера:

openssl s_client -connect mydomain.com:443 -servername mydomain.com < /dev/null

Прочитайте раздел Certificate chain в начале вывода. Ваш сертификат должен появиться на глубине 0, а промежуточный — на глубине 1. Если глубина 1 отсутствует, промежуточный сертификат не попал в PEM-файл, и сайт будет работать в одних браузерах и давать сбой в других. При оценке этого игнорируйте строку Verify return code: она сообщает только о вердикте по цепочке и может показывать успех в ситуациях, никак не связанных с тем, что вы тестируете.

Затем проверьте со стороны — там, где результат отражает то, что получают реальные посетители. Наш SSL Checker сообщает о сертификате, цепочке и дате истечения срока действия.

Продление и автоматизация

Замена PEM-файла — это вся процедура продления: соберите его заново из нового сертификата и того же или нового ключа, затем выполните перезагрузку. В конфигурации HAProxy ничто не ссылается на дату истечения срока действия, так что редактировать конфигурацию не нужно, пока путь к файлу остаётся прежним.

Учитывая, что срок действия сертификатов сокращается, автоматизацию стоит настроить уже сейчас. В версии 3.2 HAProxy получил встроенный ACME-клиент, настраиваемый через раздел acme. Пока стоит относиться к нему скорее как к предварительной версии, чем как к готовой для production-инфраструктуре: он всё ещё помечен как экспериментальный в 3.4 и требует expose-experimental-directives в глобальном разделе, поддерживает только типы вызова http-01, dns-01 и dns-persist-01, а сертификаты, которые он генерирует, приходится выгружать через сокет статистики, чтобы они попали на диск. Тип dns-persist-01, добавленный в 3.4, использует статическую TXT-запись, которая устанавливается один раз и никогда не меняется между продлениями, поэтому не требует доступа на запись к API DNS-провайдера при каждом продлении. Устоявшаяся альтернатива — запустить внешний ACME-клиент и настроить его этап развёртывания на пересборку PEM-файла и перезагрузку HAProxy, что соответствует той же схеме, что описана в нашем руководстве по ACME для Apache и NGINX.

Часто задаваемые вопросы

В каком порядке размещаются сертификат, цепочка и ключ в PEM-файле?

Сначала ваш сертификат, затем все промежуточные сертификаты, затем приватный ключ. В руководстве HAProxy файл описывается как созданный путём объединения PEM-файлов, и говорится, что промежуточный сертификат может быть объединён в него. Если вы предпочитаете хранить ключ отдельно, просто исключите его и сохраните по тому же пути с добавлением .key, который HAProxy загружает автоматически.

Почему мой HTTPS-сайт перенаправляет сам на себя в цикле?

Обычно причина в строке server backend без адреса, такой как server web1 :80 check. HAProxy разрешает пустой адрес в 0.0.0.0, а этот адрес означает, что соединение перенаправляется на тот же IP-адрес, к которому подключился клиент, то есть на сам HAProxy. Поэтому backend указывает на собственный порт 80 HAProxy, который и является тем frontend, который выполняет перенаправление с HTTP на HTTPS. Присвойте каждой строке server реальный IP-адрес или имя хоста.

Могу ли я иметь две строки bind для порта 443 в одном frontend?

Можете, и в этом проблема. HAProxy не объединяет их и не предупреждает, а haproxy -c проходит проверку успешно, потому что завершается до попытки привязки. В результате вы получаете два прослушивающих сокета на одном порту с разными параметрами TLS в каждой строке, поэтому настройка, присутствующая только в одной из них, применяется лишь к части вашего трафика. Держите одну строку bind на порт и размещайте общие настройки TLS в ssl-default-bind-options.

Усиливает ли безопасность добавление ssl-min-ver TLSv1.2?

Само по себе нет. В документации HAProxy указано, что значение по умолчанию для ssl-min-ver уже равно TLSv1.2, поэтому установка того же значения лишь фиксирует ваше намерение, не меняя поведения. Реальное усиление достигается настройками шифров, и помните, что TLS 1.3 требует ssl-default-bind-ciphersuites, тогда как TLS 1.2 и ниже используют ssl-default-bind-ciphers.

Нужны ли сертификаты также моим серверам backend?

В стандартной настройке нет. HAProxy завершает TLS на границе сети и передаёт обычный HTTP, поэтому только балансировщик нагрузки хранит сертификат. Добавьте ssl verify required с файлом CA в строки server только в том случае, если ваша политика требует шифрования и на внутреннем участке.

Как обслуживать несколько доменов с одного экземпляра HAProxy?

Укажите crt на директорию вместо файла. HAProxy загружает каждый сертификат в ней и выбирает нужный для каждого запроса с помощью SNI, так что одна строка bind покрывает их все. Другой вариант — мультидоменный (SAN) сертификат, который подходит для набора имён, продлеваемых вместе.

Какую версию HAProxy следует использовать?

Ветка 3.4, выпущенная в июне 2026 года, является текущим релизом с долгосрочной поддержкой и поддерживается до второго квартала 2031 года. Ветки HAProxy с чётными номерами — это LTS-версии с примерно пятилетним сроком поддержки, тогда как нечётные ветки, такие как 3.3, получают поддержку на 12-18 месяцев. Всё, что описано в этом руководстве, применимо к версии 3.0 и более поздним; только встроенный ACME-клиент требует версии 3.2 или новее.

Если сертификат установлен, но браузеры всё равно жалуются, наши руководства по распространённым ошибкам SSL охватывают типичные причины — чаще всего это неполная цепочка или имя, которое сертификат не перечисляет.

Сэкономьте 10% на SSL-сертификатах при заказе сегодня!

Быстрая выдача, надежное шифрование, 99,99% доверия к браузеру, специализированная поддержка и 25-дневная гарантия возврата денег. Код купона: SAVE10

Детальное изображение дракона в полете
Написано

Опытный автор контента, специализирующийся на SSL-сертификатах. Превращает сложные темы кибербезопасности в понятный, увлекательный контент. Вносите свой вклад в повышение уровня цифровой безопасности с помощью впечатляющих рассказов.

Avatar of Sergiu Rosca
Technical Review by Sergiu Rosca

Sergiu Rosca is the core web developer behind SSL Dragon. He manages the technical infrastructure, platform performance, and backend integrations that keep the site running smoothly and securely. At SSL Dragon, Sergiu shares practical insights on web development, site optimization, and technical troubleshooting.

All SSL Dragon installation guides are tested on live server environments and undergo a strict peer-review process to ensure your infrastructure remains secure. Read our full Editorial Policy.