v0.2.5 - Fix AVIF blobs downloading instead of displaying: add image/avif -> .avif to centralized MIME map in bud08.c and add .avif/.bin to nginx try_files lists (remote.nginx.config, config/ginxsom-local.conf, config/local-nginx.conf); update README and NGINX_CONFIG_UPDATES docs; rebuild static binary
This commit is contained in:
@@ -200,7 +200,7 @@ server {
|
||||
}
|
||||
|
||||
# GET requests served directly with extension fallback
|
||||
try_files /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.txt /$1.md =404;
|
||||
try_files /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.avif /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.txt /$1.md /$1.bin =404;
|
||||
}
|
||||
|
||||
# Upload endpoint
|
||||
|
||||
Binary file not shown.
@@ -113,7 +113,7 @@ server {
|
||||
return 405;
|
||||
}
|
||||
|
||||
try_files /blobs/$1.html /blobs/$1.js /blobs/$1.mjs /blobs/$1.css /blobs/$1.txt /blobs/$1.jpg /blobs/$1.jpeg /blobs/$1.png /blobs/$1.webp /blobs/$1.gif /blobs/$1.pdf /blobs/$1.mp4 /blobs/$1.mp3 /blobs/$1.md =404;
|
||||
try_files /blobs/$1.html /blobs/$1.js /blobs/$1.mjs /blobs/$1.css /blobs/$1.txt /blobs/$1.jpg /blobs/$1.jpeg /blobs/$1.png /blobs/$1.webp /blobs/$1.avif /blobs/$1.gif /blobs/$1.pdf /blobs/$1.mp4 /blobs/$1.mp3 /blobs/$1.md /blobs/$1.bin =404;
|
||||
|
||||
# Declare UTF-8 on text responses: nginx derives Content-Type from
|
||||
# the file extension (.txt -> text/plain) with no charset, so
|
||||
|
||||
@@ -298,7 +298,7 @@ http {
|
||||
}
|
||||
|
||||
# GET requests - serve files directly with extension fallback
|
||||
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;
|
||||
try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.avif /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md /$1.bin =404;
|
||||
|
||||
# Declare UTF-8 on text responses: nginx derives Content-Type from
|
||||
# the file extension (.txt -> text/plain) with no charset, so
|
||||
@@ -684,7 +684,7 @@ http {
|
||||
}
|
||||
|
||||
# GET requests - serve files directly with extension fallback
|
||||
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;
|
||||
try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.avif /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md /$1.bin =404;
|
||||
|
||||
# Declare UTF-8 on text responses: nginx derives Content-Type from
|
||||
# the file extension (.txt -> text/plain) with no charset, so
|
||||
|
||||
@@ -223,7 +223,7 @@ server {
|
||||
return 405;
|
||||
}
|
||||
|
||||
try_files /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404;
|
||||
try_files /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.avif /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md /$1.bin =404;
|
||||
|
||||
# Cache headers
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
# 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`](src/bud08.c:98) drives every place a blob
|
||||
is written to disk:
|
||||
|
||||
```c
|
||||
#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()`](src/bud08.c:139) 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()`](src/main.c:1866) (BUD-02 upload)
|
||||
- [`handle_mirror_request()`](src/bud04.c:427) (BUD-04 mirror)
|
||||
- [`handle_get_request()`](src/main.c:1195) (FastCGI GET fallback)
|
||||
- [`handle_delete_request_with_validation()`](src/main.c:1654)
|
||||
- [`admin_commands.c`](src/admin_commands.c:667)
|
||||
|
||||
### 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 form
|
||||
`try_files /$1* =404` documented in [`README.md`](README.md:580) — finds the
|
||||
`.bin` file and serves it with `default_type application/octet-stream`.
|
||||
|
||||
`application/octet-stream` plus the `X-Content-Type-Options: nosniff` header
|
||||
(see [`config/ginxsom-local.conf`](config/ginxsom-local.conf:20)) forces the
|
||||
browser to download rather than render. This is the observed symptom.
|
||||
|
||||
Note: [`mime.types`](mime.types:19) **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`](src/bud08.c:98) | Add `X("image/avif", ".avif")` to the map |
|
||||
| [`config/ginxsom-local.conf`](config/ginxsom-local.conf:116) | Add `.avif` to `try_files` |
|
||||
| [`config/local-nginx.conf`](config/local-nginx.conf:301) | Add `.avif` to both `try_files` blocks (HTTP + HTTPS) |
|
||||
| [`remote.nginx.config`](remote.nginx.config:187) | Add `.avif` to `try_files` |
|
||||
| [`README.md`](README.md:203) | Update documented `try_files` examples |
|
||||
| [`docs/NGINX_CONFIG_UPDATES.md`](docs/NGINX_CONFIG_UPDATES.md:226) | Update documented `try_files` example |
|
||||
|
||||
## Fix Plan
|
||||
|
||||
### Step 1 - Backend: map `image/avif` to `.avif`
|
||||
|
||||
In [`src/bud08.c`](src/bud08.c:98), add an entry to
|
||||
`GINXSOM_MIME_EXTENSION_ENTRIES`:
|
||||
|
||||
```c
|
||||
X("image/avif", ".avif") \
|
||||
```
|
||||
|
||||
This automatically:
|
||||
- stores new AVIF uploads as `<sha256>.avif`
|
||||
- adds `image/avif` to the advertised `supported_mime_types` list 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`](README.md:203) and
|
||||
[`docs/NGINX_CONFIG_UPDATES.md`](docs/NGINX_CONFIG_UPDATES.md:226) so future
|
||||
deployments include `.avif`.
|
||||
|
||||
### Step 5 - Rebuild static binaries
|
||||
|
||||
Rebuild via [`Dockerfile.alpine-musl`](Dockerfile.alpine-musl:1) /
|
||||
[`build_static.sh`](build_static.sh) so the updated `mime_to_extension()` map is
|
||||
compiled into `build/ginxsom-fcgi_static_*`.
|
||||
|
||||
### Step 6 - Verify
|
||||
|
||||
1. Upload an AVIF with `Content-Type: image/avif`.
|
||||
2. Confirm the file on disk is `<sha256>.avif` (not `.bin`).
|
||||
3. Confirm the DB `type` column is `image/avif`.
|
||||
4. `curl -I https://<host>/<sha256>.avif` and confirm
|
||||
`Content-Type: image/avif` and no `Content-Disposition: attachment`.
|
||||
5. 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>.bin` to `<sha>.avif` for rows where
|
||||
`blobs.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()`](src/bud08.c:284) so
|
||||
NIP-94 `dim` tags are emitted for AVIF (currently only PNG/JPEG/WebP).
|
||||
- Add `avif` to the URL-extension detection in
|
||||
[`determine_blob_content_type()`](src/bud04.c:143) for BUD-04 mirroring.
|
||||
|
||||
## Why This Is Not a `mime.types` Problem
|
||||
|
||||
[`mime.types`](mime.types:19) and [`config/mime.types`](config/mime.types:19)
|
||||
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`.
|
||||
+1
-1
@@ -184,7 +184,7 @@ server {
|
||||
return 405;
|
||||
}
|
||||
|
||||
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;
|
||||
try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.avif /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md /$1.bin =404;
|
||||
|
||||
# Declare UTF-8 on text responses. nginx derives Content-Type from the
|
||||
# file extension here (e.g. .txt -> text/plain) with no charset, so
|
||||
|
||||
@@ -98,6 +98,7 @@ int nip94_get_origin(char* out, size_t out_size) {
|
||||
#define GINXSOM_MIME_EXTENSION_ENTRIES(X) \
|
||||
X("image/jpeg", ".jpg") \
|
||||
X("image/webp", ".webp") \
|
||||
X("image/avif", ".avif") \
|
||||
X("image/png", ".png") \
|
||||
X("image/gif", ".gif") \
|
||||
X("video/mp4", ".mp4") \
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
// Version information (auto-updated by build system)
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_PATCH 4
|
||||
#define VERSION "v0.2.4"
|
||||
#define VERSION_PATCH 5
|
||||
#define VERSION "v0.2.5"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
Reference in New Issue
Block a user