15 days ago
Deployment ID: 8d9b791b-0f4f-4191-8d0c-7eba89ba7bc4
Service ID: 50b95423-6326-4cd0-ac58-f6480433ea6e
Public URL: https://linkwithmitch.com/georgia-dream/
Key details:
Files are present on disk (/app/georgia-dream/index.html exists)
Caddy serves it correctly when tested directly on localhost:8080
Other paths like /pcs/ work fine both publicly and internally
CDN caching is disabled
Request: Check edge routing logs for what's happening with requests to /georgia-dream/
5 Replies
15 days ago
This thread has been opened as a bounty so the community can help solve it.
Status changed to Open Railway • 15 days ago
14 days ago
yeah already checked that, files are there: ls /app/georgia-dream/ shows both index.html and homeownership-program.html, right names and everything. also tried hitting caddy directly from inside the container (raw http over /dev/tcp to localhost:8080) and got a clean 200 back with the right content. so it's serving fine internally, just 404s once it actually goes out over the public domain. feels like something on the edge/routing side for this one path specifically, not my caddyfile. Still broken this morning too, fwiw
14 days ago
saw a note on another thread that a platform API incident was resolved, figured it might be related so I redeployed again — new deployment id 15685598, shows successful in the dashboard, but /georgia-dream/ is still 404 publicly. same as before: files are on disk, caddy serves it fine internally (confirmed via /dev/tcp to localhost:8080, clean 200), just fails once it goes out over the public domain. doesn't look like that other incident was the cause of this one. still looks like an edge routing issue tied to this specific new top-level path, would appreciate someone reviewing it to make sure it isn't an issue on my end.
14 days ago
Hey, I was able to reproduce your Railway deploy issue and good news: it's not Railway's edge, it's just a small Caddyfile configuration issue.
What's happening
Caddy can serve multiple "sites" from one Caddyfile, and it picks which site block handles a request by the domain name the request asks for (the Host header).
- Your inside-the-container test asks for
localhost. That matches alocalhost:8080block that serves your whole /app folder, so you get 200. - A visitor asks for
yourdomain.com. That does not matchlocalhost, so the request falls into a different block, and that block only routes/pcs. Caddy itself returns the 404.
Same container, same files, two different route sets depending on the domain in the request. That is why the files are on disk, localhost works, /pcs/ works everywhere, and redeploying changes nothing — the same Caddyfile ships with every deploy.
Quick check before changing anything
Run this from inside the container:
This is the /dev/tcp test you already ran, with one line changed. From inside the container:
exec 3<>/dev/tcp/localhost/8080
printf 'GET /georgia-dream/ HTTP/1.1\r\nHost: yourdomain.com\r\nConnection: close\r\n\r\n' >&3
head -1 <&3The only difference from your version is the Host: line — it now says your domain instead of localhost. Run it once as above, then again with Host: localhost.
If the first prints HTTP/1.1 404 Not Found and the second prints HTTP/1.1 200 OK, that's the proof. Same container, same port, seconds apart, and Railway's edge is not involved in either request. The only thing that changed is the domain name in the request, so the 404 is being decided inside Caddy.
(If you have curl in the image, this is the same test:
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: yourdomain.com" http://localhost:8080/georgia-dream/)
If this prints 404 while the same request without the Host header prints 200, that's the proof: same container, same port, only the domain name differs. The 404 is decided inside Caddy, not at the edge.
Fix
- Open your Caddyfile and look at the site addresses (the text before each
{). - If there is more than one site block, or any address contains a hostname (
localhost:8080, a domain name), collapse everything into one host-agnostic block:
{
auto_https off
}
http://:8080 {
root * /app
file_server
}
Move your other directives inside this one block, and keep the http:// prefix — in my tests, a hostname site address sharing the port flipped the listener to HTTPS and every plain request got 400.
- Redeploy. Both paths return 200 publicly after this.
I verified both halves on Railway with caddy 2.10.0: the split config produced your exact symptom (public /georgia-dream/ 404, public /pcs/ 200, both 200 internally — and the 404 carried the same Railway edge headers as the 200s, so the edge handled them identically). After switching to the single block, everything returned 200 publicly.
If your Caddyfile is already a single block with no hostname in it, paste it here along with the output of the curl check above and I'll take another look.
Hope this helps :)
mayori
Try run `ls /app`, is the file there?
an hour ago
Thanks for checking in, confirmed again: ls /app/georgia-dream shows both files, present and correctly named. that part's been checked a few times now, it's not a missing-file issue. the actual evidence is the internal vs public split: caddy returns a clean 200 for that path when hit directly from inside the container (localhost:8080), but the exact same path 404s once it goes out over the public domain. same container, same files, different result depending on whether the request comes from inside or outside.
for now we're working around it by not using a brand-new top-level folder, so this isn't urgent on our end anymore. but wanted to flag that we do have plans to add more top-level sections down the line, so if there's ever time to actually dig into why a new top-level folder specifically doesn't route publicly (while new files inside existing folders deploy fine), we'd still love to understand what's going on, just no longer blocking us day to day