The board was fine, you turned the orange cloud on, and now every visitor gets ERR_TOO_MANY_REDIRECTS. Nothing in the container logs explains it, because nothing in the container is wrong. Cloudflare is set to Flexible, so it fetches your origin over plain HTTP, your server redirects plain HTTP to HTTPS, and round it goes. Change the encryption mode.
Cloudflare's behaviour below was read from Cloudflare's own troubleshooting documentation on 23 August 2026.
Why does switching the proxy on create a loop?
Because two independent decisions about HTTPS are being made by two systems that do not know about each other. The browser talks to Cloudflare, and Cloudflare talks to your origin, and the encryption mode governs only the second of those. Cloudflare states what Flexible means without hedging:
If your domain's encryption mode is set to Flexible, Cloudflare sends unencrypted requests to your origin server over HTTP.
Cloudflare, ERR_TOO_MANY_REDIRECTS
Now look at your origin. Caddy issues certificates and redirects HTTP to HTTPS on its own, with no configuration at all, and an nginx set up by Certbot gets a server block whose only job is that redirect. Every guide you followed left you with a server that insists on HTTPS, which is correct and which you want.
Put those together and the sequence is: the browser asks Cloudflare over HTTPS, Cloudflare asks your origin over HTTP, your origin answers 301 Moved Permanently to the HTTPS address, Cloudflare passes that redirect back, the browser asks again, and Cloudflare asks your origin over HTTP again. Nobody errors. Every party is behaving exactly as configured. The browser is the only one keeping count, which is why the logs are so unhelpful and why the same symptom on a self-hosted app behind a proxy ran to 24 comments on Coolify's repository before it was closed.
How do you confirm it before changing anything?
Three commands, and you will know which half is redirecting.
1. Watch the loop from outside.
curl -sSI -L --max-redirs 5 https://board.example.com/ \
| grep -iE '^(HTTP|location)'
The same status line and the same location header repeating is the loop. If you see one redirect and then a 200, this is not your problem.
2. Ask the origin directly, bypassing Cloudflare, substituting your server's real address:
curl -sSI --resolve board.example.com:80:203.0.113.10 \
http://board.example.com/ | grep -iE '^(HTTP|location)'
A 301 to the HTTPS address here is the origin's half of the loop. That answer is correct. It is only a problem because of what is asking.
3. Check the origin actually serves HTTPS with a valid certificate, because the fix depends on it:
curl -sSI --resolve board.example.com:443:203.0.113.10 \
https://board.example.com/ | head -1
A clean status line means you have a real certificate at the origin and can move straight to the fix. A certificate error means you need one first.
What is the fix?
1. Set the encryption mode to Full (strict). In the Cloudflare dashboard, under SSL/TLS and then Overview. Cloudflare will connect to your origin over HTTPS and validate the certificate it presents, which is what your origin was expecting all along. The loop ends on the next request.
2. If the origin has no publicly trusted certificate, issue one before you switch. Cloudflare's Origin CA issues a free certificate that is trusted by Cloudflare specifically, which is enough for this hop. Install it at the origin, then set Full (strict).
3. Do not fix it by deleting the origin's HTTPS redirect. Cloudflare's page does offer that as an alternative, and it genuinely stops the loop, but it leaves the hop between Cloudflare and your server unencrypted and means anyone reaching your origin directly is served over plain HTTP. It is a reasonable stopgap while you sort out a certificate, not a destination.
4. Purge the Cloudflare cache and retest in a private window. A 301 is a permanent redirect and browsers cache it hard, so a fixed site can keep looping in the tab you have been testing in.
Cloudflare's own page names three families of cause, and it is worth seeing all of them next to each other, because the fix flips depending on which direction the origin redirects:
| What is set | The loop it creates | What to change |
|---|---|---|
| Flexible encryption mode | Cloudflare fetches over HTTP, the origin redirects to HTTPS | Full (strict), with a certificate at the origin |
| Full or Full (strict), origin redirecting HTTPS back to HTTP | Cloudflare fetches over HTTPS, the origin sends it back down | Remove the HTTP redirect at the origin |
| Always Use HTTPS, origin redirecting HTTPS to HTTP | The same loop driven from the edge instead | Fix the origin, or turn Always Use HTTPS off |
| A redirect rule whose destination matches its source | Apex to www while DNS sends www straight back | Rewrite the rule so the two differ |
How do you tell it worked?
Run the first command again. One redirect, or none, followed by a 200, is the answer you want:
curl -sSI https://board.example.com/ | grep -iE '^(HTTP|cf-ray)'
The cf-ray header confirms the request really went through Cloudflare rather than round it. Then load the board in a fresh private window, sign in, and cast a vote, because a session cookie marked Secure is the thing most likely to still be unhappy after a scheme change. If the page loads signed out and loops signed in, the remaining fault is in the headers your proxy sends the application, which is the mixed content problem rather than this one.
One payment, no subscription, unlimited products.
Same error, different cause
| What you see | What it usually is |
|---|---|
| It loops through a Cloudflare Tunnel with no orange cloud involved | The tunnel's ingress points at http://localhost while the local server forces HTTPS, or at https://localhost with a certificate the tunnel will not accept. Same argument, different transport |
| It still loops after switching to Full (strict) | HSTS. The browser was told to always use HTTPS and is honouring it against an origin that redirects the other way. Test in a fresh profile, and check the origin is not sending its own downgrade |
| Only the apex or only the www hostname loops | A redirect rule pointing one at the other while DNS or the origin sends it back. Follow the chain with curl -L and read every location line |
| Only some paths loop | An application-level canonical redirect, for example forcing a trailing slash or a lower-case path, disagreeing with a rule at the edge |
| Turning the orange cloud off fixes it instantly | Confirms the diagnosis rather than solving it. That is a valid rollback while you install a certificate, and grey-clouding the record is also what several hosted vendors ask for during domain verification |
| It works for you and loops for a colleague | A cached redirect on their side, or an HSTS entry. Ask them to test in a private window before you change anything |
Does this one apply to a static support centre?
Yes, and unlike most of what goes wrong in front of a self-hosted tool, this one is not about the application at all. The fault lives between the browser and your host, so a static site hits it exactly as hard as a container does: if your host forces HTTPS, and any sensible static host does, and the mode is Flexible, you get the same loop with no application involved. Docket has no special protection here. The related setup steps, including the grey cloud during domain verification, are in custom domains for a support site and Cloudflare Tunnel for a support site.
Frequently asked questions
What is the difference between Flexible, Full and Full (strict)?
They describe the Cloudflare to origin hop only. Flexible connects over plain HTTP. Full connects over HTTPS but accepts any certificate, including a self-signed one. Full (strict) connects over HTTPS and validates the certificate properly. Full (strict) is the one to aim for, and Cloudflare's own Origin CA gives you a free certificate that satisfies it.
Why did it work before I switched the proxy on?
Because with the orange cloud off, Cloudflare only answers DNS and the browser connects straight to your server over HTTPS. There is no second hop and therefore no second opinion about the scheme. Proxying introduces that hop, and the encryption mode is where you tell Cloudflare what to do with it.
Is Flexible mode ever the right choice?
Rarely, and never for anything with a login. It leaves the connection between Cloudflare and your server unencrypted, so traffic that visitors see as secure is not secure for its whole journey. If the only thing standing between you and Full (strict) is a certificate, an Origin CA certificate takes minutes to issue and install.
Why do the container logs show nothing wrong?
Because nothing is wrong in the container. It receives a plain HTTP request and answers with a redirect, correctly, every time. There is no error to log. The loop only exists when you look at the whole chain, which is why the diagnosis has to be made with curl from outside rather than from inside the box.
Could this be my proxy rather than Cloudflare?
It can be, and the second command above tells you which. Ask the origin directly with --resolve. If the origin returns a single redirect to HTTPS and then serves a page, it is behaving; the loop is being created by the settings in front of it. If the origin loops against itself, fix that first and leave Cloudflare alone until it does not.