Behaviour
Every page load on https://opake.at logs two errors:
GET /assets/index-N4vJjP7P.css → 404
- React hydration mismatch (#418) on the stylesheet
<link>
The page still renders styled because the client router injects the correct stylesheet (index-BMkwwaKQ.css, 200) after hydration. Impact is one dead request plus a hydration warning per page view — cosmetic today, but the hydration mismatch masks any future real ones.
Evidence
- The SSR HTML's
modulepreload hashes all match the deployed image's client assets (all 200), so the serving pod is the current image — only the stylesheet URL diverges.
- The CI build log for the deployed image emits
dist/client/assets/index-BMkwwaKQ.css and never produces index-N4vJjP7P.css. The SSR environment does not write assets (ssr.emitAssets defaults to false), so the URL baked into the server router chunk is a hash computed over the SSR pass's own CSS output — which diverged from the client pass.
- Behaviour is identical across both replicas; not a rollout or cache-skew artefact.
Reproduction attempts (all negative)
Local builds resolve the SSR ?url to the client's hash in every variant tried:
| Variant |
Server ref |
Client CSS |
| isolated linker, node |
index-BMkwwaKQ.css |
index-BMkwwaKQ.css |
--linker=hoisted, node |
same |
same |
--linker=hoisted, bun run --bun |
same |
same |
hoisted + TZ=UTC, no locale |
same |
same |
Client CSS is byte-identical between CI and local (same hash), so the divergence is confined to the SSR pass and only occurs in the containerised build. Remaining suspects: the wasm-bindgen glue generated fresh in the wasm-builder stage feeding the SSR pass's Tailwind candidate scan differently from the client pass, or an environment-specific @tailwindcss/vite SSR behaviour.
Next steps
- Inspect
dist/server/assets/router-*.js inside the published image (rg.fr-par.scw.cloud/opake/web:prod-e7fd83f…) to confirm the baked URL, and diff the SSR pass's CSS against the client's in an instrumented CI run.
- Candidate fixes once confirmed: resolve the root stylesheet URL from the client manifest instead of the SSR bundle's own
?url emission, or force the SSR pass to reuse the client CSS output.
Behaviour
Every page load on https://opake.at logs two errors:
GET /assets/index-N4vJjP7P.css→ 404<link>The page still renders styled because the client router injects the correct stylesheet (
index-BMkwwaKQ.css, 200) after hydration. Impact is one dead request plus a hydration warning per page view — cosmetic today, but the hydration mismatch masks any future real ones.Evidence
modulepreloadhashes all match the deployed image's client assets (all 200), so the serving pod is the current image — only the stylesheet URL diverges.dist/client/assets/index-BMkwwaKQ.cssand never producesindex-N4vJjP7P.css. The SSR environment does not write assets (ssr.emitAssetsdefaults to false), so the URL baked into the server router chunk is a hash computed over the SSR pass's own CSS output — which diverged from the client pass.Reproduction attempts (all negative)
Local builds resolve the SSR
?urlto the client's hash in every variant tried:index-BMkwwaKQ.cssindex-BMkwwaKQ.css--linker=hoisted, node--linker=hoisted,bun run --bunTZ=UTC, no localeClient CSS is byte-identical between CI and local (same hash), so the divergence is confined to the SSR pass and only occurs in the containerised build. Remaining suspects: the wasm-bindgen glue generated fresh in the
wasm-builderstage feeding the SSR pass's Tailwind candidate scan differently from the client pass, or an environment-specific@tailwindcss/viteSSR behaviour.Next steps
dist/server/assets/router-*.jsinside the published image (rg.fr-par.scw.cloud/opake/web:prod-e7fd83f…) to confirm the baked URL, and diff the SSR pass's CSS against the client's in an instrumented CI run.?urlemission, or force the SSR pass to reuse the client CSS output.