-
-
Notifications
You must be signed in to change notification settings - Fork 172
Static files served with Last-Modified = now() instead of the file mtime #1323
Description
Every static file is served with Last-Modified set to SystemTime::now() rather than the file's real modification time. The header therefore changes on every request and carries no cache-validation information. There is also no ETag or Cache-Control, and the local-filesystem and database-filesystem "modified since" comparisons disagree (> vs >=), so conditional-request behavior differs by backend.
Reproduction (in-memory SQLite, no other setup)
mkdir /tmp/sp && cd /tmp/sp printf 'hello static world\n' > asset.txt touch -t 202001010000 asset.txt # file mtime is 2020年01月01日 SQLPAGE_DATABASE_URL='sqlite::memory:' sqlpage & curl -sI http://localhost:8080/asset.txt | grep -i last-modified sleep 1 curl -sI http://localhost:8080/asset.txt | grep -i last-modified
Observed (SQLPage v0.44.1), despite the file mtime being 2020年01月01日:
last-modified: 2026年6月12日 12:02:22 GMT
last-modified: 2026年6月12日 12:02:23 GMT
The value is "now" and advances by one second between the two requests. A client doing If-Modified-Since revalidation can never get a meaningful answer, and caches cannot key on it.
Root cause
Last-Modified is built from SystemTime::now() when serving a file: src/webserver/http.rs#L469.
The comparison contract also differs between backends: the local filesystem uses strict > (src/filesystem.rs#L254) while the database filesystem query uses >= (src/filesystem.rs#L303), so a file modified in the same second as the last check is treated differently depending on where it is stored.
Suggested fix
Set Last-Modified from the actual source mtime (the value is already read for cache validation in modified_since), or omit the header when the mtime is unknown. Pick one comparison contract (> or >=) and use it for both the local and DB filesystems. Optionally add ETag / Cache-Control for static assets while here.
Risk and mitigation
Low. Correct Last-Modified strictly improves caching and revalidation. The mtime is available for both local files (fs::metadata) and DB-backed files (the last_modified column on sqlpage_files), so no information is missing.
Split out from #1249 (item 3).