bg-tutorials

Cómo configurar un certificado SSL en HAProxy

HAProxy es un balanceador de carga TCP y HTTP que termina TLS, es decir, descifra el tráfico en el borde y reenvía HTTP simple a tus servidores backend. Eso lo convierte en la única máquina de la ruta que necesita el certificado, y lo quiere en una forma específica: un solo archivo, que contenga el certificado, los intermedios y la clave privada.

Esta guía cubre todo el recorrido: generar un CSR, ensamblar el archivo PEM, escribir el frontend y el backend, comprobar la configuración y recargar sin cortar las conexiones activas. Los comandos se ejecutaron con HAProxy 3.4, la rama actual de soporte a largo plazo, lanzada en junio de 2026 y mantenida hasta el segundo trimestre de 2031.

Lo que espera HAProxy: un único archivo PEM

La mayoría de servidores toman el certificado, la cadena y la clave como tres ajustes separados. HAProxy toma uno solo. Su manual de configuración describe la palabra clave crt como la que designa «un archivo PEM que contiene tanto los certificados requeridos como cualquier clave privada asociada», construido concatenando archivos PEM, y añade que «si tu CA requiere un certificado intermedio, este también puede concatenarse en este archivo».

Vale la pena conocer dos comportamientos antes de empezar, porque ahorran trabajo más adelante:

  • La clave puede vivir junto al certificado en lugar de dentro de él. Si el archivo no contiene una clave privada, HAProxy busca la misma ruta con .key añadido. Así que mydomain.pem más mydomain.pem.key funciona igual que un archivo combinado.
  • Puedes apuntar crt a un directorio. HAProxy carga todos los archivos que encuentra allí y selecciona el certificado correcto por solicitud usando SNI. Así es como se sirven varios sitios desde un solo frontend sin una línea bind para cada uno.

Generar el CSR y la clave privada

Un CSR es la solicitud codificada que entregas a la Autoridad de Certificación. Genéralo en la máquina HAProxy, o en cualquier lugar donde puedas mantener segura la clave privada, ya que la clave nunca sale de tu lado.

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"

La línea -addext no es opcional en la práctica. Los navegadores dejaron de comparar los nombres de host con el Common Name hace años y solo leen el Subject Alternative Name, así que un CSR que lleva únicamente un CN produce un certificado que falla en todos los navegadores actuales. Lista todos los nombres que el certificado debe cubrir, incluyendo el dominio simple y la forma www si sirves ambos.

Omite las opciones -subj y -addext si prefieres que se te pregunte por cada campo. De cualquier forma terminas con dos archivos: mydomain.csr para enviar, y mydomain.key para conservar. Confirma que el CSR contiene lo que esperas antes de enviarlo, ya sea con nuestro decodificador de CSR o localmente:

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

Comprueba que las entradas SAN estén listadas y que la firma se verifique. Si prefieres no usar la línea de comandos en absoluto, nuestro generador de CSR produce el mismo par en el navegador. Más antecedentes sobre los comandos subyacentes están en nuestra guía de comandos OpenSSL.

Consigue que se emita el certificado

Envía el CSR a la CA, elige el tipo de certificado que corresponda a lo que estás protegiendo, y completa la validación. Un certificado de dominio único cubre un nombre de host, un comodín (wildcard) cubre todos los subdominios de primer nivel, y un certificado multidominio (SAN) cubre una lista de nombres no relacionados. Detrás de un balanceador de carga, el multidominio es la opción habitual, ya que una instancia de HAProxy suele estar delante de varios sitios.

La CA devuelve un archivo comprimido que contiene tu certificado y la cadena intermedia, normalmente como un archivo CA bundle. Ambos ya están en formato PEM, que es lo que necesitas. Planifica la renovación ahora en lugar de después: desde el 15 de marzo de 2026, un certificado TLS de confianza pública puede tener una validez de no más de 200 días, bajando a 100 días en marzo de 2027 y 47 días en marzo de 2029, así que el reemplazo manual dejará de ser práctico bastante pronto.

Construye el archivo PEM que leerá HAProxy

Crea un directorio para los certificados y ensambla el archivo allí. Mantén todo en un solo lugar desde el principio; repartir esto entre un directorio personal, /etc/haproxy y /etc/ssl es la forma en que la gente termina editando un archivo mientras HAProxy lee otro.

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

Concatena el certificado, luego los intermedios, luego la clave privada. La redirección debe ejecutarse como root, así que usa una tubería hacia tee en lugar de escribir sudo cat ... > /etc/haproxy/certs/..., que falla con un error de permisos porque el shell abre el archivo de salida como tu propio usuario antes de que sudo llegue a ejecutarse:

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

Ese archivo ahora contiene tu clave privada en texto plano, así que blíndalo antes de seguir adelante:

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

Los permisos solo para root son correctos aquí, no un obstáculo. HAProxy se inicia con privilegios de superusuario, lo cual, según señala su manual, es necesario para que después pueda cambiar a su propio usuario sin privilegios, y lee el certificado durante ese arranque. No hace falta ampliar los permisos para que el usuario haproxy pueda leer la clave, y no deberías hacerlo.

Si construiste el CSR en otra máquina, copia primero los archivos y luego elimina las copias del directorio de tránsito:

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

Configura HAProxy

Abre /etc/haproxy/haproxy.cfg en un editor de terminal en el propio servidor, como nano o vim. Edítalo directamente en lugar de en una estación de trabajo, para no estar nunca recargando un archivo distinto al que probaste.

El frontend

Un solo frontend puede aceptar tanto HTTP simple como HTTPS. Vincula el puerto 80 para la redirección, vincula el puerto 443 con el certificado, y envía todo lo demás al 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 ofrece HTTP/2 y recurre a HTTP/1.1 si es necesario. La redirección se activa solo cuando la solicitud no llegó por TLS, que es lo que comprueba ssl_fc, así que las solicitudes en el puerto 443 pasan directamente.

Pon todas las opciones TLS en esa única línea bind

Aquí es donde las configuraciones de HAProxy más a menudo salen mal, y falla en silencio. Las guías presentan con frecuencia el endurecimiento TLS como un segundo paso, mostrando una línea bind nueva para el puerto 443 con opciones adicionales. Si añades esa línea en lugar de editar la existente, terminas con dos líneas bind para el mismo puerto, y HAProxy no se queja. Arranca, y abre dos sockets de escucha separados en el puerto 443 con configuraciones TLS distintas. A qué socket llega una conexión dada no es algo que controles, así que tu endurecimiento cubre aproximadamente la mitad de tu tráfico.

La comprobación de configuración tampoco detecta esto, y su propia documentación explica por qué: -c «solo realiza una comprobación de los archivos de configuración y termina antes de intentar hacer bind«. Un listener duplicado es una condición que ocurre en el momento del bind, así que una comprobación de sintaxis nunca la verá.

Establece valores globales por defecto, de modo que todas las líneas bind del archivo los hereden y no haya nada que duplicar:

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

Vale la pena saberlo antes de copiar ese bloque en cualquier sitio: ssl-min-ver ya toma por defecto TLSv1.2, en palabras del propio HAProxy. Ponerlo en TLSv1.2 no cambia nada y solo documenta la intención, lo cual está bien, pero no es la mejora de seguridad que a menudo se presenta como tal. Los dos ajustes de cifrado sí hacen un trabajo real, y son separados a propósito: ssl-default-bind-ciphers se aplica a TLS 1.2 e inferiores, ssl-default-bind-ciphersuites se aplica a TLS 1.3. Configura solo el primero y tus conjuntos TLS 1.3 se quedarán en sus valores predeterminados.

El backend

Dale a cada servidor una dirección real. Esto importa más de lo que parece:

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

Una línea server escrita como server web1 :80 check, sin la dirección, no produce un error. HAProxy la acepta y resuelve la dirección faltante como 0.0.0.0, que su manual trata como un valor especial que significa que la conexión se reenvía a la misma dirección IP a la que el cliente se conectó. Esa dirección es el propio HAProxy, así que el backend termina apuntando silenciosamente al puerto 80 del propio HAProxy, que es el frontend que acabas de configurar. El tráfico vuelve a entrar en la redirección en lugar de llegar a ninguna aplicación, y las comprobaciones de salud aparecen como saludables porque realmente hay algo escuchando. Si un balanceador de carga responde a cada solicitud HTTPS con una redirección hacia sí mismo, revisa primero las líneas server.

Como HAProxy termina TLS, los backends reciben HTTP simple en el puerto 80 y no necesitan su propio certificado. Si la política requiere cifrado también en ese tramo, añade ssl verify required y un archivo de CA a las líneas server y apúntalas al puerto 443 en su lugar.

Pasa el esquema original al backend. Dado que el backend ahora recibe HTTP simple, una aplicación que genera URL absolutas, como hacen WordPress, Django y Rails, las generará con http://, lo que se manifiesta como contenido mixto o como un bucle de redirección creado por la propia aplicación. Añade una línea al frontend:

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

y otra al backend, que además pasa la dirección IP real del cliente:

option forwardfor

Después, a la mayoría de frameworks hay que decirles que confíen en esas cabeceras; esa parte se configura en la aplicación, no en HAProxy.

Comprueba la configuración antes de aplicarla

Nunca reinicies un balanceador de carga con un archivo sin verificar. Valida primero:

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

Con -V imprime Configuration file is valid en caso de éxito y devuelve un código de salida cero. Sin ella, el éxito es silencioso. Cualquier advertencia se reporta independientemente de si el archivo es válido o no, así que lee la salida en lugar de confiar en la ausencia de texto en rojo.

Recarga en lugar de reiniciar

sudo systemctl reload haproxy

La distinción es real en un balanceador de carga. Una recarga inicia un proceso nuevo y le indica al antiguo que «termine lo que está haciendo y se retire», así que las solicitudes ya en curso se completan con normalidad. Un reinicio le indica al proceso antiguo que «termine inmediatamente sin acabar lo que estaba haciendo», lo que corta las conexiones activas, incluidas subidas de archivos y llamadas API de larga duración. Usa el reinicio solo cuando una recarga no pueda aplicar el cambio, como después de alterar los ajustes de proceso de la sección global.

Confirma que el servicio volvió y está escuchando en ambos puertos:

sudo systemctl status haproxy
sudo ss -tlnp | grep haproxy

Exactamente un socket de escucha por puerto es lo que se busca aquí. Dos en el puerto 443 significa que tienes la línea bind duplicada descrita anteriormente.

Verifica que el certificado se está sirviendo

Comprueba lo que HAProxy realmente presenta, incluida la cadena, desde el propio servidor:

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

Lee la sección Certificate chain en la parte superior de la salida. Tu certificado debería aparecer en el nivel 0 y el intermedio en el nivel 1. Si falta el nivel 1, el intermedio nunca llegó al archivo PEM, y el sitio funcionará en algunos navegadores mientras falla en otros. Ignora la línea Verify return code al evaluar esto: solo reporta el veredicto de la cadena, y puede leerse como exitosa en situaciones que no tienen nada que ver con lo que estás probando.

Luego confírmalo desde el exterior, donde el resultado refleja lo que reciben los visitantes reales. Nuestro SSL Checker reporta el certificado, la cadena y la fecha de caducidad.

Renovaciones y automatización

Reemplazar el archivo PEM es todo el procedimiento de renovación: reconstrúyelo a partir del nuevo certificado y la misma clave o una nueva, luego recarga. Nada en la configuración de HAProxy hace referencia a la fecha de caducidad, así que no se necesita ninguna edición de configuración mientras la ruta del archivo se mantenga igual.

Con los tiempos de vida de los certificados acortándose, vale la pena configurar la automatización ahora. HAProxy obtuvo un cliente ACME integrado en la versión 3.2, configurado mediante una sección acme. Trátalo como una vista previa más que como infraestructura de producción por el momento: todavía está marcado como experimental en la 3.4 y requiere expose-experimental-directives en la sección global, solo admite los tipos de desafío http-01, dns-01 y dns-persist-01, y los certificados que genera deben volcarse desde el socket de estadísticas para llegar al disco. El tipo dns-persist-01, añadido en la 3.4, usa un registro TXT estático que se configura una vez y nunca cambia entre renovaciones, así que no requiere acceso de escritura a la API del proveedor DNS en cada renovación. La alternativa consolidada es ejecutar un cliente ACME externo y hacer que su paso de despliegue reconstruya el archivo PEM y recargue HAProxy, que es el mismo patrón descrito en nuestra guía de ACME para Apache y NGINX.

Preguntas frecuentes

¿En qué orden van el certificado, la cadena y la clave en el archivo PEM?

Primero tu certificado, luego los intermedios, luego la clave privada. El manual de HAProxy describe el archivo como uno construido concatenando archivos PEM y dice que el intermedio puede concatenarse en él. Si prefieres mantener la clave separada, omítela por completo y guárdala como la misma ruta con .key añadido, que HAProxy carga automáticamente.

¿Por qué mi sitio HTTPS se redirige a sí mismo en un bucle?

Normalmente es una línea server del backend sin dirección, como server web1 :80 check. HAProxy resuelve la dirección vacía como 0.0.0.0, y esa dirección significa que la conexión se reenvía a la misma dirección IP a la que se conectó el cliente, que es el propio HAProxy. El backend, por lo tanto, apunta al propio puerto 80 de HAProxy, que es el frontend que emite la redirección de HTTP a HTTPS. Dale a cada línea server una dirección IP o nombre de host real.

¿Puedo tener dos líneas bind para el puerto 443 en el mismo frontend?

Puedes, y ese es el problema. HAProxy ni las combina ni avisa, y haproxy -c pasa porque termina antes de intentar hacer bind. Obtienes dos sockets de escucha en un puerto con las opciones TLS diferentes que lleve cada línea, así que un ajuste presente solo en una de ellas se aplica solo a parte de tu tráfico. Mantén una línea bind por puerto y coloca los ajustes TLS compartidos en ssl-default-bind-options.

¿Añadir ssl-min-ver TLSv1.2 endurece algo?

No por sí solo. HAProxy documenta que el valor predeterminado de ssl-min-ver ya es TLSv1.2, así que configurarlo con el mismo valor solo deja constancia de tu intención sin cambiar el comportamiento. El endurecimiento real proviene de los ajustes de cifrado, y recuerda que TLS 1.3 necesita ssl-default-bind-ciphersuites mientras que TLS 1.2 e inferiores usan ssl-default-bind-ciphers.

¿Mis servidores backend también necesitan certificados?

No en la configuración estándar. HAProxy termina TLS en el borde y reenvía HTTP simple, por lo que solo el balanceador de carga posee el certificado. Añade ssl verify required con un archivo de CA en las líneas server solo si tu política requiere que ese tramo interno también esté cifrado.

¿Cómo sirvo varios dominios desde una instancia de HAProxy?

Apunta crt a un directorio en lugar de a un archivo. HAProxy carga todos los certificados que contiene y elige el correcto por solicitud usando SNI, así que una sola línea bind los cubre todos. Un certificado multidominio (SAN) es la otra vía, y se adapta a un conjunto de nombres que se renuevan juntos.

¿Qué versión de HAProxy debería estar usando?

La rama 3.4, lanzada en junio de 2026, es la versión actual de soporte a largo plazo y se mantiene hasta el segundo trimestre de 2031. Las ramas de número par de HAProxy son las LTS, con aproximadamente cinco años de mantenimiento, mientras que las ramas de número impar como la 3.3 reciben de 12 a 18 meses. Todo lo que se explica en esta guía se aplica a partir de la 3.0; solo el cliente ACME integrado requiere la 3.2 o posterior.

Si el certificado está instalado pero los navegadores siguen mostrando errores, nuestras guías sobre errores comunes de SSL cubren las causas habituales, la mayoría de las veces una cadena incompleta o un nombre que el certificado no incluye.

Ahorre un 10% en certificados SSL al realizar su pedido hoy mismo.

Emisión rápida, cifrado potente, 99,99% de confianza del navegador, asistencia dedicada y garantía de devolución del dinero en 25 días. Código del cupón: SAVE10

Una imagen detallada de un dragón en vuelo
Escrito por

Redactor de contenidos experimentado especializado en Certificados SSL. Transformar temas complejos de ciberseguridad en contenido claro y atractivo. Contribuir a mejorar la seguridad digital a través de narrativas impactantes.

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.