Nuno
Nuno

Why an HTTPS Redirect Broke Certificate Issuing at Our Edge

Our edge could not issue one TLS certificate because port 80 already redirected to HTTPS. How I fixed it without a proxy, and a new AWS account surprise.
Vander Nunes
October 06, 2026
XInstagramYouTubeLinkedIn
Diagram of traffic on ports 80 and 443 reaching a host, with one path redirected away from the service that requested the certificate

The edge has to serve domains it does not own

redir.rocks is a redirect service I run with my co-founder. He writes the application. I built the infrastructure, the edge, observability and operations.

Customers point their own domains at redir.rocks. When a visitor opens one of those domains, the edge has to answer over HTTPS with a valid certificate for that exact name, then send the visitor where the customer configured. So the edge needs a certificate for every customer domain, issued automatically when the domain is added, and picked by name (SNI) on each new connection.

In our design the application's gateway does that itself. It terminates TLS, picks the certificate by SNI, and gets certificates from the CA with the ACME HTTP-01 challenge. HTTP-01 is simple: the CA makes a plain HTTP request to the domain on port 80, at a well-known path, and expects a specific token in the response.

Port 80 was already taken

On the same host, Caddy was already running. It owned ports 80 and 443, and like most modern servers it redirected every plain HTTP request to HTTPS.

So when the CA came to validate a customer domain on port 80, the request reached Caddy, not the gateway. Caddy answered with a 308 redirect to HTTPS. The token lived in the gateway, which never saw the request. Certificate issuing never worked. Not once.

It is an easy mistake to make because each part was correct on its own. Redirecting HTTP to HTTPS is the right default. HTTP-01 on port 80 is the right challenge for domains you do not control in DNS. They just cannot share the same port on the same address without someone deciding who answers.

Why I did not put a proxy in front

The obvious fix is a proxy on 80 and 443 that routes by host name: challenge requests and customer domains to the gateway, everything else to Caddy as before. Caddy can do that. So can an nginx stream proxy.

The problem is the client IP. Every redirect is counted for analytics and billing, and analytics needs the real visitor address. A layer 4 proxy in front of the gateway would make every connection look like it came from localhost. The usual answer is the PROXY protocol, where the proxy prepends the original address. But the Elixir HTTP server the application uses, Bandit, cannot read the PROXY protocol. So a proxy would have worked for certificates and silently broken analytics.

A second address and a DNAT rule

What I did instead was give the gateway its own address.

The host got a secondary private IP, with a second Elastic IP attached to it. Customer domains point at that second address. On the host, an nftables rule in the prerouting hook rewrites the destination of traffic to that address so it lands on the port the gateway listens on. Caddy keeps the first address and is not touched at all. Because DNAT only changes the destination, the source address stays the real client IP.

The shape of the rule, with placeholder addresses and ports:

table ip edge {
  chain prerouting {
    type nat hook prerouting priority dstnat; policy accept;
    ip daddr 10.0.1.20 tcp dport 80 dnat to 10.0.1.20:4080
    ip daddr 10.0.1.20 tcp dport 443 dnat to 10.0.1.20:4443
  }
}

There was one more wrinkle. The deploy tool alternates the gateway between two ports on each release. A static DNAT rule would point at the wrong port half the time. A small timer checked which port the live gateway was on and re-pointed the rule every 20 seconds.

It worked. Certificates were issued, Caddy did not change, and analytics kept the client IP. It was also a little fragile: up to 20 seconds of pointing at the wrong port after a deploy is not something I wanted to keep.

Moving to a load balancer

The next step was a Network Load Balancer with client IP preservation in front of the gateway. An NLB passes TCP through without terminating TLS, so the gateway still picks certificates by SNI, and with client IP preservation on, the gateway sees the visitor's real address. Health checks replace the timer: the NLB only sends traffic to the port that answers.

It costs more than an nftables rule, because an NLB charges every hour. I accepted that. The edge is the product, and paying a fixed amount a month for health-checked routing is a fair trade against a hand-built rule I had to remember.

The surprise: a new account cannot create load balancers

The cutover on 5 October 2026 cost about 4 minutes of edge downtime, and it was my fault in a specific way.

redir.rocks runs in its own, fairly new AWS account. A brand-new AWS account cannot create load balancers until AWS Support enables them. terraform plan does not know that. The plan looked fine. The apply failed on the load balancer.

By then I had already detached the Elastic IP from the instance, because the plan was to move it to the NLB. So for about 4 minutes there was no edge.

Two lessons. First, in a new AWS account, create a throwaway version of every resource type you will need early, and see which ones are blocked. Load balancers are the one that caught me. Second, never take away the old path before the new one exists. Detaching the address should have been the last step, after the NLB was up and healthy.

Checklist for an edge that issues certificates

  • Decide who owns port 80 on each address before you add HTTP-01.
  • If something redirects HTTP to HTTPS, make sure challenge paths reach the service that requested the certificate.
  • Check whether your application server reads the PROXY protocol before you put a layer 4 proxy in front.
  • In a new AWS account, test-create load balancers and other resource types early.
  • Build the new path, check it is healthy, and only then remove the old one.

If you run an edge with customer domains and certificates and want a second pair of eyes on it, get in touch.