Coolify + Traefik: Fix a “weird” www redirect (307 / XML / duplicate responses) the clean way
TL;DR: If www.yourdomain.com is doing cursed things on Coolify (307s, odd content-types, messy chains), the fix is usually to make one layer responsible for redirects and make it permanent (301/308). You can do that either in-app (fast) or proxy-only (Traefik dynamic config).
If you’ve deployed a site on Coolify and suddenly www.yourdomain.com starts acting weird — returning a temporary redirect (307), sometimes showing the “wrong” Content-Type (e.g. application/xml), or looking like it sends two responses — you’re not alone. This often shows up when multiple routing/redirect rules overlap across Coolify-generated Traefik config plus your app (or another proxy/CDN).
The annoying part is that Coolify generates Traefik routers/middlewares for you. Even if you spot the exact middleware setting you’d want to change (like a redirect being temporary), editing the generated labels/config is not a stable workflow because it can be regenerated/overwritten on redeploy. Coolify’s own docs basically nudge you toward “configure redirects the supported way,” not by hand-editing whatever got generated.
This post shows two solutions (app-level redirect or Traefik dynamic config), plus the exact curl commands to verify everything is clean for crawlers and humans.
What’s actually happening?
Coolify sits in front of your app with a reverse proxy (usually Traefik). When you add both domains (apex + www) and pick a redirect direction, Coolify wires routers and redirect middlewares.
If that redirect ends up being temporary (307/302), you can get:
- Search engines treating
wwwand non-wwwas separate for longer than you want (Google explicitly recommends permanent redirects for permanent moves: 301/308). - Inconsistent caching / redirect chains.
- “Double redirect” symptoms if you also redirect inside the app or another proxy layer.
Also: a 307 is, by definition, a temporary redirect. A 308 is a permanent redirect (same idea as 301, but preserves method semantics). If you want www → apex to be the canonical permanent move, you generally want 301 or 308.
Reproduce the problem with curl
Start simple:
curl -I https://www.yourdomain.com/
If you see 307 Temporary Redirect (or anything that isn’t clearly permanent), you’re in the right place.
To see the full redirect chain:
curl -IL https://www.yourdomain.com/
The ideal end state is:
- Exactly one redirect hop from
www → apex - Status code 301 or 308
- Final response is
200on the canonical host (https://yourdomain.com/) withContent-Type: text/html
Fix option A: allow both hosts in Coolify and redirect inside the app
This is the easiest way to stop fighting proxy-generated behavior. You let Coolify accept both hosts, then you make your app enforce the canonical host with a permanent redirect.
First, stop Coolify from doing the www redirect for you.
In Coolify → your app → Domains/General, set it to “allow both” (or remove any forced redirect direction), then redeploy.
Then add a permanent redirect in your app. Example for Astro middleware:
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async (ctx, next) => {
const host = (ctx.request.headers.get("host") || "").toLowerCase();
if (host === "www.yourdomain.com") {
const url = new URL(ctx.request.url);
url.host = "yourdomain.com";
url.protocol = "https:";
return Response.redirect(url.toString(), 308);
}
return next();
});
Deploy, then verify:
curl -IL https://www.yourdomain.com/
Why 308? Google treats 301 and 308 as permanent redirects, and 308 is the “permanent” sibling of 307. It’s a clean choice for canonicalization.
Pros:
- Quick and boring (in the good way).
- Works even if Coolify’s proxy rules change later.
- You can version-control the redirect logic.
Cons:
- Redirect is handled by the app (not purely proxy-side).
Fix option B: proxy-only with Traefik dynamic config
If you want the redirect entirely at the proxy level (no app code), you can do it in Traefik dynamic configuration — assuming you control the Traefik instance behind Coolify.
First, make the app serve only the canonical host.
In your app’s Domains, keep only:
https://yourdomain.com
Remove www from the app, so Coolify stops generating app routers for it.
Then add a Traefik dynamic config that catches www and redirects permanently. Example YAML:
http:
routers:
www-http:
rule: "Host(`www.yourdomain.com`)"
entryPoints: ["http"]
middlewares: ["redirect-https", "redirect-nonwww"]
service: noop@internal
www-https:
rule: "Host(`www.yourdomain.com`)"
entryPoints: ["https"]
middlewares: ["redirect-nonwww"]
service: noop@internal
tls:
certResolver: letsencrypt
middlewares:
redirect-nonwww:
redirectRegex:
regex: "^https?://www\\.(.+)"
replacement: "https://${1}"
permanent: true
redirect-https:
redirectScheme:
scheme: https
permanent: true
Then verify:
curl -IL https://www.yourdomain.com/
Pros:
- Clean canonical redirect, proxy-only.
- No framework-specific behavior.
- One consistent redirect authority at the edge.
Cons:
- Requires access to Traefik dynamic config on the server.
- Slightly more moving parts.
The one mistake that causes most “weird” behavior
Don’t redirect in two places.
If you have:
- Coolify/Traefik redirecting
www → apex - Your app redirecting
www → apex - Another layer (Cloudflare rules, Caddy, Nginx) redirecting too
…you can end up with inconsistent status codes, headers, or confusing “double response” symptoms in tooling.
Pick one redirect authority and keep it boring.
Quick checklist for SEO-friendly redirects
After your fix, confirm:
https://www.yourdomain.com/returns 301/308 → https://yourdomain.com/http://yourdomain.com/redirects to httpshttp://www.yourdomain.com/redirects cleanly (one hop is ideal; two hops is acceptable)- If you use canonical tags, they point to the apex host consistently
- Search Console is set up for the canonical host and your sitemap is served there
Final verification commands
Run these and keep the output as your sanity check:
curl -IL https://www.yourdomain.com/
curl -IL http://www.yourdomain.com/
curl -IL https://yourdomain.com/
If the redirect chain is clean and the final Content-Type is HTML on the canonical host, you’re done.
Key takeaways
- Coolify generates proxy config; avoid “hand-editing generated stuff” as your long-term strategy.
- Use one redirect layer (app or proxy), make it permanent (301/308), and keep the chain short.
- Verify with
curl -ILuntil the chain is boring.