Fixing HTTPS Redirect Loops: Pangolin + Dokploy + Traefik
When exposing services through a tunnel like Pangolin, you might hit a frustrating HTTPS redirect loop. Here's how I solved it for FreeScout on Dokploy, and the solution applies to any Laravel/PHP app behind this stack.
The Setup
Internet → Pangolin (TLS termination) → Newt → Traefik → Container
Pangolin terminates TLS and forwards requests with X-Forwarded-Proto: https. Simple enough, right?
The Problem
The app was stuck in an infinite redirect loop. Every request to HTTPS redirected to... HTTPS. Over and over.
After hours of debugging, I discovered the culprit: Traefik overwrites X-Forwarded-Proto.
When Newt connects to Traefik via HTTP (internal Docker network), Traefik sees an HTTP request and sets X-Forwarded-Proto: http — completely ignoring what Pangolin sent.
The app sees X-Forwarded-Proto: http, thinks “this should be HTTPS”, and redirects. Loop.
The Fix
Two changes are needed:
1. Tell Traefik to Trust Internal Networks
Edit /etc/dokploy/traefik/traefik.yml:
entryPoints:
web:
address: ':80'
forwardedHeaders:
trustedIPs:
- "10.0.0.0/8"
- "172.16.0.0/12"
websecure:
address: ':443'
http:
tls:
certResolver: letsencrypt
forwardedHeaders:
trustedIPs:
- "10.0.0.0/8"
- "172.16.0.0/12"
This tells Traefik: “If a request comes from a Docker internal network, trust its X-Forwarded-* headers.”
Restart Traefik:
docker service update --force dokploy-traefik_traefik
2. Tell Laravel to Trust the Proxy
In Dokploy, add this environment variable:
APP_TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12
This configures Laravel's TrustProxies middleware to accept forwarded headers from Docker networks.
Why This Works
- Pangolin sends
X-Forwarded-Proto: https - Newt forwards to Traefik
- Traefik sees Newt's IP is trusted → preserves the header
- App receives correct
X-Forwarded-Proto: https - No redirect. Done.
The Beautiful Part
This is a one-time configuration that works for all services exposed via Pangolin. No per-service hacks needed.
What Didn't Work
Before finding this solution, I tried:
- Direct container routing — bypasses Traefik but requires per-service network configuration
- Custom Traefik middleware — Dokploy overwrites dynamic configs
- Various app-level settings —
APP_FORCE_HTTPS, nginx fastcgi params, etc.
The Traefik forwardedHeaders.trustedIPs setting is the proper, general solution.
Key Takeaway
When debugging proxy header issues, check every hop in your chain. The problem isn't always where you think it is. In this case, Traefik's default behavior of overwriting headers was the silent culprit.