5.9 KiB
Fix: .avif files download instead of displaying
Symptom
Browsers download .avif blobs instead of rendering them inline. Other image
types (.jpg, .png, .webp, .gif) display correctly.
Root Cause
There are two independent gaps in the AVIF path, and they compound.
1. Backend MIME to extension map is missing image/avif
The centralized map in src/bud08.c drives every place a blob
is written to disk:
#define GINXSOM_MIME_EXTENSION_ENTRIES(X) \
X("image/jpeg", ".jpg") \
X("image/webp", ".webp") \
X("image/png", ".png") \
X("image/gif", ".gif") \
... (no image/avif) ...
mime_to_extension() falls through to ".bin" for any
unmapped type. So an AVIF upload is stored on disk as <sha256>.bin, while the
database records type = "image/avif". The stored extension and the recorded
MIME type disagree.
This map is used by:
handle_upload_request_with_validation()(BUD-02 upload)handle_mirror_request()(BUD-04 mirror)handle_get_request()(FastCGI GET fallback)handle_delete_request_with_validation()admin_commands.c
2. nginx try_files extension lists are missing .avif
GET requests for blobs are served directly by nginx from disk (not FastCGI). nginx resolves the file by trying a hardcoded list of extensions:
try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg
/$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404;
.avif is not in the list, and neither is .bin. Consequences:
- If the blob was stored as
<sha>.avif, nginx never finds it (404). - If the blob was stored as
<sha>.bin(the current AVIF behavior), nginx either 404s, or — in deployments using the wildcard formtry_files /$1* =404documented inREADME.md— finds the.binfile and serves it withdefault_type application/octet-stream.
application/octet-stream plus the X-Content-Type-Options: nosniff header
(see config/ginxsom-local.conf) forces the
browser to download rather than render. This is the observed symptom.
Note: mime.types already contains
image/avif avif;, so once the file is stored with a .avif extension and
nginx is allowed to find it, the correct Content-Type: image/avif is emitted
automatically. The MIME table is not the problem.
Affected Files
| File | Change |
|---|---|
src/bud08.c |
Add X("image/avif", ".avif") to the map |
config/ginxsom-local.conf |
Add .avif to try_files |
config/local-nginx.conf |
Add .avif to both try_files blocks (HTTP + HTTPS) |
remote.nginx.config |
Add .avif to try_files |
README.md |
Update documented try_files examples |
docs/NGINX_CONFIG_UPDATES.md |
Update documented try_files example |
Fix Plan
Step 1 - Backend: map image/avif to .avif
In src/bud08.c, add an entry to
GINXSOM_MIME_EXTENSION_ENTRIES:
X("image/avif", ".avif") \
This automatically:
- stores new AVIF uploads as
<sha256>.avif - adds
image/avifto the advertisedsupported_mime_typeslist in the NIP-11 root response - makes NIP-94 canonical URLs end in
.avif
Step 2 - nginx: add .avif to every try_files list
Add /$1.avif (or /blobs/$1.avif in the local conf) to each list. Place it
next to the other image extensions for readability.
Step 3 - nginx: add .bin to try_files (related robustness fix)
Blobs whose MIME type is unknown are stored as .bin. They are currently
unreachable through the explicit try_files lists. Adding /$1.bin lets them
be served (as application/octet-stream, which is correct for unknown types).
This is optional but closes the same class of bug.
Step 4 - Update documentation
Update the try_files examples in README.md and
docs/NGINX_CONFIG_UPDATES.md so future
deployments include .avif.
Step 5 - Rebuild static binaries
Rebuild via Dockerfile.alpine-musl /
build_static.sh so the updated mime_to_extension() map is
compiled into build/ginxsom-fcgi_static_*.
Step 6 - Verify
- Upload an AVIF with
Content-Type: image/avif. - Confirm the file on disk is
<sha256>.avif(not.bin). - Confirm the DB
typecolumn isimage/avif. curl -I https://<host>/<sha256>.avifand confirmContent-Type: image/avifand noContent-Disposition: attachment.- Load the URL in a browser and confirm it renders inline.
Important Caveat: Existing AVIF Blobs
Config and code changes only affect newly uploaded AVIF blobs. Blobs already
stored as <sha256>.bin will not be found by the new .avif try_files entry.
Options for existing data:
- Re-upload the affected AVIF blobs (simplest, content-addressed so the hash/URL is unchanged).
- One-off migration: rename
<sha>.binto<sha>.aviffor rows whereblobs.type = 'image/avif'. Requires care because the DB is authoritative for blob existence.
Optional Enhancement (out of scope unless requested)
- Add AVIF dimension parsing to
nip94_get_dimensions()so NIP-94dimtags are emitted for AVIF (currently only PNG/JPEG/WebP). - Add
avifto the URL-extension detection indetermine_blob_content_type()for BUD-04 mirroring.
Why This Is Not a mime.types Problem
mime.types and config/mime.types
both already map avif to image/avif. The failure is upstream: the file is
stored with the wrong extension and/or nginx is not told to look for .avif.