Skip to content
SEO

Let’s Encrypt Behind a Reverse Proxy: Getting Certificates Issued

Let's Encrypt Behind a Reverse Proxy: Getting Certificates Issued
In this article
  1. Why does certificate renewal fail after moving behind a proxy?
  2. Decide first: certificate on the origin or on the proxy?
  3. Routing the ACME challenge path in NGINX for HTTP-01
  4. DNS-01 challenge: validation that ignores the proxy
  5. Checks before the next renewal
  6. Picking a Let’s Encrypt reverse proxy setup that keeps renewing
  7. FAQ

A Let’s Encrypt reverse proxy setup usually stops issuing certificates for one boring reason: the validation request now lands on the proxy, not on your origin. There are three fixes. Forward the ACME challenge path to the origin, move the certificate to the proxy, or switch to DNS-01 validation.

The story is almost always the same. Renewal ran fine for months. Then someone pointed the domain’s A record at a proxy, and the next attempt fell over. Below you’ll find how to track down the cause, where the certificate should actually live, how to set up NGINX for the challenge path, and DNS-01 (the route that behaves the same whether there’s a proxy in front or not).

Why does certificate renewal fail after moving behind a proxy?

Because HTTP-01 validation fetches a file from whatever IP address the domain resolves to. And that address now belongs to the proxy. So the token your origin wrote never gets served. The Let’s Encrypt challenge types documentation walks through the mechanics: the ACME client drops a token file at http://<domain>/.well-known/acme-challenge/<TOKEN>, then Let’s Encrypt fetches it, possibly several times and from several vantage points. Anything between the public hostname and that file that behaves differently? Validation breaks.

The client log tells you which flavour you’ve got. A 404 or an “unauthorized” error. A redirect to HTTPS the proxy can’t complete. Or just a timeout. One more thing: a failed validation means starting over with a new order, so fix the path first and retry after, not the other way round.

Decide first: certificate on the origin or on the proxy?

Where TLS terminates decides where the certificate lives and which challenge makes sense. Keep the certificate on the origin and the proxy just passes traffic through, the origin keeps its ACME client, and the challenge path has to reach it. Move it to the proxy and whoever runs TLS there also runs the ACME client. The origin then exposes nothing publicly for validation.

  • Who renews: you on the origin, or the proxy operator on the proxy.
  • What breaks when the proxy changes: an origin certificate depends on the proxy forwarding the challenge path, while a proxy certificate has to be reissued wherever TLS moves.
  • Wildcards: need one? Plan for DNS-01 either way.

Your call. But before you commit, ask the proxy operator how they handle TLS and the /.well-known path (you’d be surprised how often nobody has asked). And if you’re still not sure a proxy belongs in front of the site at all, weigh the case for proxying first.

Routing the ACME challenge path in NGINX for HTTP-01

On the origin, serve /.well-known/acme-challenge/ over plain HTTP and put it above any HTTPS redirect. Then a request forwarded by the proxy actually finds the token. On your own origin:

  1. Add a location block for the challenge path that points to a webroot.
  2. Keep that block out of the HTTP to HTTPS redirect.
  3. Point the ACME client at the same webroot, for example certbot certonly --webroot -w /var/www/acme -d example.com.
  4. Test the configuration with nginx -t and reload NGINX.
server {
    listen 80;
    server_name example.com;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/acme;
        default_type text/plain;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Testing is simple. Drop a file into /var/www/acme/.well-known/acme-challenge/, request it through the public hostname from another network, and check that the origin’s access log recorded the hit. The catch: this only works if the proxy forwards plain HTTP requests for that path untouched. So check the proxy side too.

DNS-01 challenge: validation that ignores the proxy

DNS-01 proves you control the domain with a TXT record at _acme-challenge.<domain>, which means HTTP routing through the proxy stops mattering. Is it harder to set up than HTTP-01? Yes. But it covers cases HTTP-01 simply can’t, and it issues wildcard certificates. You’ll need a DNS provider with an API, or you delegate _acme-challenge by CNAME or NS to a zone that updates quickly.

Propagation is where people get bitten. The client should wait until the record is visible everywhere before it triggers validation. According to the DNS-01 section of the docs, if your provider gives you no way to check propagation, you may need to wait often as much as an hour. Aha, and clean out old TXT records, because Let’s Encrypt rejects a response that gets too large.

Checks before the next renewal

Run a dry-run renewal and check the challenge path or TXT record from outside your network, well before the certificate is anywhere near expiry. certbot renew --dry-run runs the whole flow against the staging environment and leaves your live certificate alone.

  • DNS resolves where you expect, to the proxy or to the origin.
  • The challenge URL returns the token over plain HTTP from an outside network.
  • No redirect loop between the proxy and the origin.
  • A deploy hook reloads NGINX after renewal, such as --deploy-hook "systemctl reload nginx".
  • Expiry monitoring alerts you, independently of the ACME client.

Every issued certificate ends up in public logs, so your hostnames leave certificate transparency footprints worth a look now and then. Still planning the proxy layer? Compare Jalvo reverse proxy plans, and weigh shared versus dedicated IPs for the addresses sitting in front of your origin.

Picking a Let’s Encrypt reverse proxy setup that keeps renewing

Three options work. Keep the certificate on the origin and forward the challenge path. Manage the certificate on the proxy. Or go with DNS-01 when you can’t control HTTP routing or need a wildcard. My advice: pick the one whose moving parts you actually control, and test it before the renewal window, not in the middle of it. Our hosting setup articles cover the configuration around it. A Let’s Encrypt reverse proxy setup renews reliably once validation has a path that doesn’t rely on guesswork.

FAQ

Can I keep HTTP-01 if the proxy forces HTTPS?

Yes, as long as the challenge path stays reachable. Let’s Encrypt follows a redirect, but validation only succeeds if the target really serves the token. A broken certificate or a redirect loop on the HTTPS side still kills it. Exempting the path from the redirect is the safer bet.

Do I need DNS-01 for a wildcard certificate?

Yes. The Let’s Encrypt challenge documentation lists wildcard issuance under DNS-01, and HTTP-01 can’t validate a wildcard name. Plan for a DNS API or a delegated _acme-challenge zone.

Why did validation pass on one server but fail through the proxy?

Because Let’s Encrypt checks the public hostname from several vantage points. A local curl on the origin tells you nothing about the route through the proxy. Test through the public DNS name from an outside network, and confirm the request shows up in the origin’s access log.

ShareLinkedInX
Web Systems team

The team that builds and runs Jalvo.

Give each site its own IP.

Add your first domain in a few minutes.

Start now

Choose which cookies we may use. Necessary cookies keep the site and your login working and cannot be switched off.

Scroll to Top