Let’s Encrypt certificates behind a Load Balancer

Let’s Encrypt and Certbot make issuing and renewing TLS certificates easy — until your servers sit behind a load balancer like HAProxy, NGINX, or Traefik. Suddenly, what used to be a simple certbot renew becomes a frustrating challenge.

You might run into issues like:

  • HTTP-01 challenges being blocked or misrouted by your reverse proxy
  • HTTPS redirects interfering with challenge resolution
  • ACME validation paths not reaching the correct backend
  • DNS-01 failing because your DNS provider doesn’t support ACME automation

Result: You can’t re-issue your certificate — or only with brittle, manual workarounds.

Why ACME Challenges Fail Behind Load Balancers

Let’s Encrypt uses ACME challenges to verify domain ownership. In practice, that’s often one of two methods:

🔸 HTTP-01 (via /.well-known/acme-challenge/)

This method fails when:

  • The load balancer terminates SSL and doesn’t forward HTTP paths
  • HTTPS redirection breaks the challenge
  • The app server never receives the request

🔸 DNS-01 (via TXT records)

This is more reliable, if:

  • Your DNS provider supports API automation
  • You have full control over DNS (often not the case in enterprise)

In short: if ACME validation fails, you’re stuck — unless you can transfer an already-valid certificate.

The Simple Alternative: Transfer the Certificates Directly

Instead of reissuing a new cert, you can securely copy existing Let’s Encrypt certificates from one server to another. This is especially useful when:

  • Migrating from an old VPS or container
  • Spinning up a new staging or backup instance
  • Your domain is behind a reverse proxy or load balancer
  • DNS-based challenges are not possible to automate

No ACME involved. No renewal issues. Just copy what already works.

Use the Ready-Made Script

To simplify the transfer, I’ve published a small Bash script on GitHub:

What the Script Does:

  • Transfers all essential Let’s Encrypt files using rsync over SSH:
    • /etc/letsencrypt/live/
    • /etc/letsencrypt/archive/
    • /etc/letsencrypt/renewal/
  • Preserves folder structure, permissions, and symbolic links
  • Can optionally reload your web server (e.g. nginx, apache) after import

The goal: allow the target server to continue using the certificate without revalidation — including future certbot renew operations.

How to use

  • Download the script cd /root && git clone https://github.com/filipnet/letsencrypt-transfer.git
  • Use the script by its input parameters in e.g. /etc/crontab
  • Or create a “helper-script” to do some more actions vim /root/letsencrypt-transfer/transfer-certs-lb.sh

Sample Helper Script

echo "Starting LetsEncrypt certificate transfer"
# LetsEncrypt Archive
/root/letsencrypt-transfer/letsencrypt-transfer-certs.sh lb.domain.de /etc/letsencrypt/archive/lb.domain.de/ /etc/letsencrypt/archive/lb.domain.de/ /root/rsa-key/id_rsa_lb.domain.de.pem
# LetsEncrypt Live (Notice that the live-folder only use SYMLINKS)
/root/letsencrypt-transfer/letsencrypt-transfer-certs.sh lb.domain.de /etc/letsencrypt/live/lb.domain.de/ /etc/letsencrypt/live/lb.domain.de/ /root/rsa-key/id_rsa_lb.domain.de.pem

echo "Restart Apache"
apachectl graceful

echo "Restart Dovecot"
systemctl reload dovecot

echo "Restart Postfix"
systemctl reload postfix

Security Considerations

  • The archive contains private keys. Use secure SSH channels only.
  • Don’t share or store the archive permanently.

Final Thoughts

Behind a proxy or load balancer, Let’s Encrypt renewal can easily break — especially if ACME challenges aren’t reliably passed through. Rather than fight the config or depend on DNS APIs you don’t control, you can transfer the certificate directly.

It’s simple, safe, and scriptable — and lets you keep your infrastructure running smoothly without risking downtime or validation errors.