Stock web image serves build-time metadata for mounted static overrides, breaking favicon and OG images #180

Closed
opened 2026-11-02 15:43:06 +00:00 by eleith-gitea-agent · 1 comment

Summary

After migrating hi.eleith.com to the stock Luzzle 0.0.245 images, custom static images mounted into the web container produce incomplete HTTP responses. The response body is the mounted image, but Content-Length still describes the stock image shipped in the build. This prevents the favicon from loading and also affects the default Open Graph image.

This should be fixed in Luzzle's runtime static-asset handling, rather than requiring instances to maintain proxy workarounds or custom application builds.

Environment

  • Luzzle web 0.0.245, stock production image
  • @sveltejs/adapter-node 6.0.1 / @sveltejs/kit 3.1.1, per the release's apps/web/package.json
  • Same-origin app assets, with custom static files bind-mounted read-only
  • Site: https://hi.eleith.com

Relevant Compose mounts (present in both local and production configurations):

volumes:
  - ./static/images/favicon.png:/app/build/client/images/favicon.png:ro
  - ./static/images/opengraph.png:/app/build/client/images/opengraph.png:ro
  - ./static/fonts/noto-sans.woff2:/app/build/client/fonts/noto-sans.woff2:ro

Reproduction

  1. Start the stock 0.0.245 web image with a custom favicon of a different size mounted at the path above.
  2. Request /images/favicon.png through the stock proxy, or open a page referencing that icon.
  3. Compare the advertised Content-Length with the actual response bytes.

For the affected deployment:

curl --http1.1 -v https://hi.eleith.com/images/favicon.png -o /tmp/favicon.png

The transfer fails because the body is shorter than the advertised length. An HTTP/2 curl request likewise fails with an incomplete-stream error.

Observed evidence

Asset Response Content-Length Actual bytes received Stock file size in 0.0.245
/images/favicon.png 33,797 4,506 33,797
/images/opengraph.png 3,474,868 158,879 3,474,868
/fonts/noto-sans.woff2 39,412 39,412 39,412
  • The page correctly includes <link rel="icon" href="/images/favicon.png"/>.
  • The received favicon bytes match the local mounted file exactly (SHA-256 0eb535cbe3b14a0b3b916016bbd14b9a2274f8a55c5fea977e0c1a0617a6673d). The mount itself works.
  • The incorrect lengths exactly match the release's stock images, verified against the repository files at @luzzle/web@0.0.245.
  • Requests with fresh query strings show the same mismatch. This is not resolved by a browser/proxy cache purge.

Root cause

Adapter-node 6.0.1 measures static files during the build and emits their sizes and ETags into build/adapter-node.js. Runtime bind mounts replace the file contents without updating this compiled asset table. The server therefore streams the replacement bytes using metadata for the original files.

Source: https://unpkg.com/@sveltejs/adapter-node@6.0.1/index.js (measure_files, create_asset_table, and generation of adapter-node.js).

Expected behavior / requested fix

Provide a supported runtime mechanism for custom static assets in stock images. When files are overridden, serve metadata matching the actual runtime content, including correct Content-Length and ETag, and ensure any precompressed variants are consistent with the override.

Please add regression coverage with differently sized favicon and Open Graph overrides; validate complete downloads, content hashes, conditional requests, and compressed responses where applicable. Existing framework JS/CSS should remain unaffected.

## Summary After migrating hi.eleith.com to the stock Luzzle 0.0.245 images, custom static images mounted into the web container produce incomplete HTTP responses. The response body is the mounted image, but `Content-Length` still describes the stock image shipped in the build. This prevents the favicon from loading and also affects the default Open Graph image. This should be fixed in Luzzle's runtime static-asset handling, rather than requiring instances to maintain proxy workarounds or custom application builds. ## Environment - Luzzle web 0.0.245, stock production image - `@sveltejs/adapter-node` 6.0.1 / `@sveltejs/kit` 3.1.1, per the release's `apps/web/package.json` - Same-origin app assets, with custom static files bind-mounted read-only - Site: https://hi.eleith.com Relevant Compose mounts (present in both local and production configurations): ```yaml volumes: - ./static/images/favicon.png:/app/build/client/images/favicon.png:ro - ./static/images/opengraph.png:/app/build/client/images/opengraph.png:ro - ./static/fonts/noto-sans.woff2:/app/build/client/fonts/noto-sans.woff2:ro ``` ## Reproduction 1. Start the stock 0.0.245 web image with a custom favicon of a different size mounted at the path above. 2. Request `/images/favicon.png` through the stock proxy, or open a page referencing that icon. 3. Compare the advertised `Content-Length` with the actual response bytes. For the affected deployment: ```sh curl --http1.1 -v https://hi.eleith.com/images/favicon.png -o /tmp/favicon.png ``` The transfer fails because the body is shorter than the advertised length. An HTTP/2 curl request likewise fails with an incomplete-stream error. ## Observed evidence | Asset | Response Content-Length | Actual bytes received | Stock file size in 0.0.245 | | --- | ---: | ---: | ---: | | `/images/favicon.png` | 33,797 | 4,506 | 33,797 | | `/images/opengraph.png` | 3,474,868 | 158,879 | 3,474,868 | | `/fonts/noto-sans.woff2` | 39,412 | 39,412 | 39,412 | - The page correctly includes `<link rel="icon" href="/images/favicon.png"/>`. - The received favicon bytes match the local mounted file exactly (SHA-256 `0eb535cbe3b14a0b3b916016bbd14b9a2274f8a55c5fea977e0c1a0617a6673d`). The mount itself works. - The incorrect lengths exactly match the release's stock images, verified against the repository files at `@luzzle/web@0.0.245`. - Requests with fresh query strings show the same mismatch. This is not resolved by a browser/proxy cache purge. ## Root cause Adapter-node 6.0.1 measures static files during the build and emits their sizes and ETags into `build/adapter-node.js`. Runtime bind mounts replace the file contents without updating this compiled asset table. The server therefore streams the replacement bytes using metadata for the original files. Source: https://unpkg.com/@sveltejs/adapter-node@6.0.1/index.js (`measure_files`, `create_asset_table`, and generation of `adapter-node.js`). ## Expected behavior / requested fix Provide a supported runtime mechanism for custom static assets in stock images. When files are overridden, serve metadata matching the actual runtime content, including correct Content-Length and ETag, and ensure any precompressed variants are consistent with the override. Please add regression coverage with differently sized favicon and Open Graph overrides; validate complete downloads, content hashes, conditional requests, and compressed responses where applicable. Existing framework JS/CSS should remain unaffected.

Fixed in commit 64524a71, landed via #181. Site favicon and default Open Graph images now support runtime configuration without replacing build output.

Fixed in commit [64524a71](https://git.eleith.com/eleith/luzzle/commit/64524a71819d2a19a65d96e05021946390d8c567), landed via #181. Site favicon and default Open Graph images now support runtime configuration without replacing build output.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: eleith/luzzle#180