Pular para o conteúdo

Configurar HAProxy: proxy reverso e webhooks no AlmaLinux 10

Por Equipe Técnica AviraHost · 13 min de leitura · Atualizado em · HAProxy, proxy-reverso, webhooks, AlmaLinux, balanceamento, AviraHost · 0

Configurar o HAProxy como proxy reverso no AlmaLinux 10 para receber webhooks de pagamento consiste em instalar o balanceador, realizar a SSL termination com certificado Let's Encrypt, editar o haproxy.cfg com frontends, backends e health checks, garantindo alta disponibilidade sem alterar o body usado na HMAC. Para ter o ambiente em produção, siga estes passos:

  1. Instale o HAProxy no AlmaLinux 10 e habilite o serviço no systemd.
  2. Libere 80/443 no firewalld e permita o HAProxy conectar aos backends no SELinux.
  3. Monte o frontend HTTPS, os backends e o health check HTTP.
  4. Preserve Host, X-Forwarded-For, Content-Type e o body original.
  5. Desative retry automático de POST e valide com curl e logs.
  6. Teste failover derrubando um worker e conferindo o segundo backend.

Pré-requisitos

  • AlmaLinux 10 com acesso root ou sudo, conforme Acessando servidores VPS Linux da AviraHost.
  • Pacote haproxy do AppStream, firewalld ativo e SELinux em enforcing.
  • Certificado PEM (certificado + cadeia + chave) em arquivo único para seudominio.com.br.
  • Dois ou mais workers HTTP na porta 8080 (PHP 8.4, Node ou similar) com rota POST de webhook e GET /health.
  • DNS A de seudominio.com.br apontando para 203.0.113.10; veja o Guia de zona DNS: registros A, MX, CNAME e TXT do zero.
  • Lista de IPs do provedor de pagamento, se for filtrar origem no frontend.

Instalação e configuração inicial do HAProxy

O proxy reverso HAProxy no AlmaLinux 10 escuta 443, escolhe o backend e devolve a resposta do worker sem reescrever o POST. Ao rodar a instalação pelo AppStream, o unit systemd já vem com User=haproxy e o arquivo principal em /etc/haproxy/haproxy.cfg. Não misture outro proxy na mesma porta: o bind exclusivo evita 502 e "address already in use".

Atualize o sistema e instale o pacote. Em seguida habilite o serviço, mas só inicie depois de validar a configuração, para o unit não falhar no primeiro boot.

dnf install -y haproxy
systemctl enable haproxy
haproxy -v
ss -tlnp | grep -E ':80|:443' || true
HAProxy version 3.x
Loaded plugins: ...
complete

Crie o diretório de certificados e junte cadeia e chave no formato que o bind ssl crt espera. O usuário haproxy precisa ler o PEM; permissão 640 e grupo haproxy bastam.

mkdir -p /etc/haproxy/certs
cat /etc/letsencrypt/live/seudominio.com.br/fullchain.pem /etc/letsencrypt/live/seudominio.com.br/privkey.pem > /etc/haproxy/certs/seudominio.com.br.pem
chown root:haproxy /etc/haproxy/certs/seudominio.com.br.pem
chmod 640 /etc/haproxy/certs/seudominio.com.br.pem

Libere HTTP e HTTPS no firewalld e recarregue. Sem isso o provedor de pagamento nunca alcança o frontend, mesmo com o processo no ar.

firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-services
success
dhcpv6-client http https ssh

Com SELinux em enforcing, o daemon só conecta a portas típicas. Workers em 8080 exigem haproxy_connect_any ou porta rotulada. Sem isso o health check marca o servidor DOWN e o webhook some do log do aplicativo.

setsebool -P haproxy_connect_any on
getsebool haproxy_connect_any
haproxy_connect_any --> on

Faça backup do cfg padrão antes de substituir. Um bind inválido impede o start; o diagnóstico de unit falho está em Como solucionar problemas de inicialização de serviços no VPS Linux: Guia Completo.

Atenção: o próximo bloco sobrescreve /etc/haproxy/haproxy.cfg. Copie o arquivo atual para haproxy.cfg.bak.

cp -a /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak

Terminação TLS no frontend e roteamento do path

A terminação TLS no HAProxy concentra um certificado, inspeciona o path /webhooks/pagamento e encaminha só o necessário aos workers. Passe TLS ponta a ponta somente se o gateway exigir o certificado no aplicativo; nesse caso o bind não usa crt e o backend fala HTTPS. Em ambos os modos use HTTP/1.1, timeouts curtos e encaminhe Host e X-Forwarded-For. Redirecionar 80 para 443 no frontend evita POST em claro; o fluxo HTTP para HTTPS também aparece em Como redirecionar um site http para https?.

Grave a configuração mínima. option forwardfor insere o IP do cliente. http-request del-header remove hop-by-hop. Não use http-request replace-path no POST do webhook: qualquer mudança de path ou query quebra a assinatura em vários provedores.

global
    log stdout format raw local0
    maxconn 4096
    user haproxy
    group haproxy
    ssl-default-bind-options ssl-min-ver TLSv1.2

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5s
    timeout client 30s
    timeout server 30s
    timeout http-request 10s
    retries 0

frontend fe_http
    bind 203.0.113.10:80
    http-request redirect scheme https unless { ssl_fc }

frontend fe_webhooks
    bind 203.0.113.10:443 ssl crt /etc/haproxy/certs/seudominio.com.br.pem alpn h2,http/1.1
    option forwardfor
    http-request set-header X-Forwarded-Proto https
    acl is_hook path_beg /webhooks/pagamento
    use_backend bk_webhooks if is_hook
    default_backend bk_webhooks

O bloco unless com ssl_fc usa chaves do ACL do HAProxy; se o validador da sua política rejeitar chaves, troque o frontend 80 por um redirect incondicional: http-request redirect location https://seudominio.com.br. O essencial é não aceitar o POST do gateway em HTTP. ALPN com h2 no frontend é aceitável; nos backends de webhook prefira HTTP/1.1 para não fragmentar o body. timeout http-request de 10s corta clientes lentos sem esperar o retry do provedor. retries 0 no defaults impede reenvio de POST quando um worker recusa a conexão no meio do handshake.

Valide a sintaxe antes do reload. Ao rodar haproxy -c você deve ver a mensagem de sucesso; qualquer aviso de crt ou bind precisa ser corrigido antes do systemd.

haproxy -c -f /etc/haproxy/haproxy.cfg
Configuration file is valid

Configurando o backend bk_webhooks para alta disponibilidade

O backend completa o proxy reverso: round-robin ou leastconn, health check GET /health e servidores com check. Sticky sessions não cabem em evento idempotente. option httpchk usa o path de saúde da API, não o path do webhook, para não gerar evento falso.

backend bk_webhooks
    balance roundrobin
    option httpchk GET /health HTTP/1.1\r\nHost:\ seudominio.com.br
    http-check expect status 200
    http-reuse never
    server app1 198.51.100.10:8080 check inter 3s fall 3 rise 2
    server app2 198.51.100.11:8080 check inter 3s fall 3 rise 2

http-reuse never reduz o risco de misturar um POST de webhook em conexão keep-alive residual. inter 3s com fall 3 tira o worker do pool em cerca de 9s. Suba o serviço e confira a porta 443.

systemctl restart haproxy
systemctl is-active haproxy
ss -tlnp | grep haproxy
active
LISTEN 0 4096 203.0.113.10:443 0.0.0.0:* users:(("haproxy",pid=2148,fd=5))
LISTEN 0 4096 203.0.113.10:80  0.0.0.0:* users:(("haproxy",pid=2148,fd=6))

Balanceamento de carga, HMAC e teste do POST

O balanceamento de carga para webhooks de pagamento deve tratar cada POST como unidade isolada: round-robin se os workers forem iguais, leastconn se o processamento for lento. Não reescreva o body nem reordene cabeçalhos da HMAC. Encaminhe o header de assinatura intacto e o Content-Type original. Filtrar IP do provedor no ACL do frontend é mais seguro do que confiar só em X-Forwarded-For no PHP.

Simule o evento com curl contra HTTPS. O -d envia o JSON cru; o header de assinatura deve chegar igual no access log da aplicação. Troque o valor de assinatura pelo formato do seu gateway (Stripe, Mercado Pago ou similar).

curl -sS -o /tmp/hook.out -w "%{http_code}\n" \
  --http1.1 \
  -X POST "https://seudominio.com.br/webhooks/pagamento" \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Signature: t=exemplo,v1=abc123" \
  --data-binary '{"id":"evt_2030113","type":"payment.updated"}'
200

No worker, confira que o raw body bate com o JSON enviado e que X-Forwarded-For mostra o IP de origem do curl (ou o IP do provedor, em produção). Se a aplicação canonicalizar JSON antes da HMAC, a validação falha mesmo com proxy correto: a correção é ler o stream bruto, não o objeto já decodificado.

Force o failover: pare o app1 e dispare de novo o POST. O health check deve marcar app1 DOWN e o app2 responder 200. Suba o app1 e espere rise 2. Para ver o estado dos servers use o socket stats se você o habilitou, ou journalctl -u haproxy -e. Um worker que responde 200 em /health mas 500 no POST continua no pool: o check não substitui idempotência no aplicativo. Trate o id do evento como chave única e ignore duplicata quando o provedor retentar após timeout. No HAProxy, não ative option redispatch nesse backend e não use retry-on 503 para POST.

Se o provedor publicar faixas de IP, restrinja o ACL no frontend (src 198.51.100.0/24) e responda 403 ao restante. Isso evita que um POST forjado chegue ao validador HMAC. Documente a faixa e revise quando o gateway atualizar a lista. Ambiente isolado em Compreendendo o Servidor VPS: O que é e Como Funciona! facilita separar o proxy dos workers.

Problemas comuns e como resolver

Sintoma: health check DOWN e webhook 503

Causa: SELinux bloqueia a conexão com 8080, /health devolve 404 ou o Host do httpchk não bate com o vhost do worker.
Solução: confirme getsebool haproxy_connect_any, teste curl http://198.51.100.10:8080/health a partir do host do HAProxy e alinhe o header Host. Depois haproxy -c e systemctl reload haproxy.

Sintoma: assinatura HMAC inválida só atrás do proxy

Causa: replace-path, compressão, troca de Content-Type ou buffer que altera o body; às vezes HTTP/2 no backend fragmenta o POST.
Solução: remova rewrites no ACL do webhook, force HTTP/1.1 no server line se preciso, desative compressão nesse backend e compare o body hexdump no worker com o payload do curl.

Sintoma: eventos duplicados após timeout

Causa: retries maior que zero, option redispatch ou timeout server menor que o processamento do pagamento, levando o provedor a retentar.
Solução: retries 0, sem redispatch, timeout server alinhado ao SLA do gateway e idempotência por id de evento na aplicação. Não faça o HAProxy reenviar POST.

Perguntas frequentes sobre HAProxy e webhooks no AlmaLinux 10

Como o HAProxy recebe webhooks de pagamento com balanceamento de carga?

O HAProxy escuta HTTPS na frente, termina ou passa o TLS e distribui POSTs entre backends com round-robin ou leastconn. Health checks evitam enviar eventos a instâncias fora do ar. Preserve o corpo e cabeçalhos originais para a assinatura do provedor.

Preciso terminar SSL no HAProxy ou no backend para webhooks?

Termine TLS no HAProxy quando quiser um único certificado e inspeção de path. Passe TLS ponta a ponta se o gateway exigir o certificado no aplicativo. Em ambos os casos, use HTTP/1.1, timeouts curtos e encaminhe X-Forwarded-For e Host.

Como validar a assinatura do webhook atrás do proxy reverso?

Não altere o body nem reordene cabeçalhos usados na HMAC. Encaminhe o header de assinatura intacto e o Content-Type original. Confirme no backend o IP de origem real via X-Forwarded-For apenas se a lista de IPs do provedor for conferida no HAProxy.

Qual algoritmo de balanceamento usar para webhooks de pagamento?

Round-robin basta se os workers forem iguais. Leastconn ajuda se o processamento for lento. Evite sticky sessions: cada evento deve ser idempotente. Marque servidor down no health check HTTP no endpoint de saúde da API.

O que fazer se o provedor retentar o webhook e duplicar o pedido?

Trate o evento como idempotente no aplicativo com ID único. No HAProxy, não reescreva o path nem faça retry automático de POST. Ajuste timeout client e server para o provedor reenviar só após falha real de conexão.

Conclusão

  • Valide haproxy.cfg com haproxy -c, recarregue o unit e confirme 443 em ss antes de cadastrar a URL no gateway.
  • Mantenha retries 0, body intacto e health check em /health, não no path do webhook.
  • Teste POST real, failover de um worker e HMAC no backend a cada mudança de certificado ou ACL.

Leia também

Precisa de ajuda com HAProxy e proxy reverso no AlmaLinux 10?

Um VPS com AlmaLinux 10, IPv4 fixo e portas 80/443 livres reduz o tempo para colocar o balanceador e os workers em produção. A equipe pode orientar dimensionamento e isolamento de rede sem alterar o payload dos webhooks.

Contratar Servidor VPS


Esta resposta foi útil?