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.
Часто задаваемые вопросы
Сначала ваш сертификат, затем все промежуточные сертификаты, затем приватный ключ. В руководстве HAProxy файл описывается как созданный путём объединения PEM-файлов, и говорится, что промежуточный сертификат может быть объединён в него. Если вы предпочитаете хранить ключ отдельно, просто исключите его и сохраните по тому же пути с добавлением .key, который HAProxy загружает автоматически.
Обычно причина в строке server backend без адреса, такой как server web1 :80 check. HAProxy разрешает пустой адрес в 0.0.0.0, а этот адрес означает, что соединение перенаправляется на тот же IP-адрес, к которому подключился клиент, то есть на сам HAProxy. Поэтому backend указывает на собственный порт 80 HAProxy, который и является тем frontend, который выполняет перенаправление с HTTP на HTTPS. Присвойте каждой строке server реальный IP-адрес или имя хоста.
Можете, и в этом проблема. HAProxy не объединяет их и не предупреждает, а haproxy -c проходит проверку успешно, потому что завершается до попытки привязки. В результате вы получаете два прослушивающих сокета на одном порту с разными параметрами TLS в каждой строке, поэтому настройка, присутствующая только в одной из них, применяется лишь к части вашего трафика. Держите одну строку bind на порт и размещайте общие настройки TLS в ssl-default-bind-options.
Само по себе нет. В документации HAProxy указано, что значение по умолчанию для ssl-min-ver уже равно TLSv1.2, поэтому установка того же значения лишь фиксирует ваше намерение, не меняя поведения. Реальное усиление достигается настройками шифров, и помните, что TLS 1.3 требует ssl-default-bind-ciphersuites, тогда как TLS 1.2 и ниже используют ssl-default-bind-ciphers.
В стандартной настройке нет. HAProxy завершает TLS на границе сети и передаёт обычный HTTP, поэтому только балансировщик нагрузки хранит сертификат. Добавьте ssl verify required с файлом CA в строки server только в том случае, если ваша политика требует шифрования и на внутреннем участке.
Укажите crt на директорию вместо файла. HAProxy загружает каждый сертификат в ней и выбирает нужный для каждого запроса с помощью SNI, так что одна строка bind покрывает их все. Другой вариант — мультидоменный (SAN) сертификат, который подходит для набора имён, продлеваемых вместе.
Ветка 3.4, выпущенная в июне 2026 года, является текущим релизом с долгосрочной поддержкой и поддерживается до второго квартала 2031 года. Ветки HAProxy с чётными номерами — это LTS-версии с примерно пятилетним сроком поддержки, тогда как нечётные ветки, такие как 3.3, получают поддержку на 12-18 месяцев. Всё, что описано в этом руководстве, применимо к версии 3.0 и более поздним; только встроенный ACME-клиент требует версии 3.2 или новее.
Если сертификат установлен, но браузеры всё равно жалуются, наши руководства по распространённым ошибкам SSL охватывают типичные причины — чаще всего это неполная цепочка или имя, которое сертификат не перечисляет.
Сэкономьте 10% на SSL-сертификатах при заказе сегодня!
Быстрая выдача, надежное шифрование, 99,99% доверия к браузеру, специализированная поддержка и 25-дневная гарантия возврата денег. Код купона: SAVE10


