A Hydration Mismatch That Was Really an Unparsed Environment Variable
TL;DR
- Deployed the site to Railway and got a 500 plus a hydration mismatch at
angus.ryer.io. - The hydration error was downstream of the real problem:
VITE_SERVER_PROXY_URLwas undefined. - Railway wasn’t interpolating a placeholder like
$server_port, so the variable arrived as a literal. - The server was running fine the whole time, which is what made this confusing.
My personal site is a Remix front end, a Go backend, and a Postgres database, all in Docker and deployed via Railway. It’s a showcase and a playground at the same time. Locally it’s genuinely pleasant — one shell script brings all the containers up together with clean output.
Deployed, angus.ryer.io gave me a hydration mismatch.
Two errors, one cause
The console showed a 500 from server-client HTML inconsistency, and separately an undefined Vite client proxy URL. I spent a while treating those as two problems.
They’re one. A hydration mismatch means the server rendered different HTML than the client expected, and an undefined environment variable is an excellent way to produce exactly that — the server renders with a URL of undefined, the client instantiates with something else, React notices they disagree.
The actual defect
Digging through the Railway deployment config, the variable wasn’t being parsed dynamically. A static placeholder — something in the shape of $server_port — had slipped through my setup and was being passed along as a literal string rather than interpolated.
Checking the logs confirmed the server itself was running. That’s the detail that cost me time: nothing was down, so I kept looking for a rendering bug instead of a configuration one.
Where I landed
The fix is to set the environment variables correctly in Railway with real dynamic allocation and the right container networking references, so the proxy resolves to something that exists.
I’d started down an IPv6 rabbit hole around internal networking before realising it was tangential. The internal network references are worth getting right, but they weren’t what was breaking this.
Worth remembering for next time: when SSR and hydration disagree, check what differs between the two environments before assuming the rendering logic is wrong. Usually something is undefined on one side.
ryer.io