Skip to content

Commit 0fca8d6

Browse files
jcormierclaude
andcommitted
nginx: cache Propshaft /assets/ with immutable, long-lived Cache-Control
Redmine 6.x+ serves core, theme and plugin assets through Propshaft at /assets/<name>-<digest>.<ext>. The content digest is the cache-buster, so match that path and serve it Cache-Control immutable for a year, adding svg/webp/font/map types and nosniff. Dynamic endpoints outside /assets/ keep hitting @redmine uncached. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0040132 commit 0fca8d6

1 file changed

Lines changed: 13 additions & 8 deletions

File tree

assets/runtime/config/nginx/redmine

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,24 @@ server {
2525
## Individual nginx logs for this Redmine vhost
2626
access_log {{REDMINE_LOG_DIR}}/nginx/redmine_access.log;
2727
error_log {{REDMINE_LOG_DIR}}/nginx/redmine_error.log;
28-
29-
# the regex logic: after either /javascripts/ or /stylesheets/ find the suffixes we want, followed by one or more numbers 0-9
30-
# This works because the files we want to cache always appear after one of those 2 directories: but not the files we want to ignore
31-
# /journals/edit/24174.js and /uploads.js?attachment_id=1&filename=my-file-to-upload.png
32-
location ~* {{REDMINE_RELATIVE_URL_ROOT}}(?<file>/(?:themes.+)?(?:javascripts|stylesheets|images|favicon).+(?:css|js|jpe?g|gif|ico|png|html)(\?[0-9]+)$) {
33-
34-
expires 24h;
28+
29+
# Static asset caching (Redmine 6.x / Propshaft).
30+
#
31+
# Redmine 6.x serves all core, theme and plugin assets through the Propshaft
32+
# pipeline at {{REDMINE_RELATIVE_URL_ROOT}}/assets/<name>-<digest>.<ext>.
33+
# The content digest in the filename is the cache-buster, so these files are
34+
# immutable and safe to cache forever.
35+
# Dynamic endpoints (issues/123.js, *.json) live outside /assets/ and keep hitting @redmine uncached.
36+
location ~* ^{{REDMINE_RELATIVE_URL_ROOT}}(?<file>/assets/.+\.(?:css|js|jpe?g|gif|ico|png|svg|webp|woff2?|ttf|otf|eot|map))$ {
37+
add_header Cache-Control "public, max-age=31536000, immutable" always;
38+
add_header X-Content-Type-Options nosniff always;
3539
gzip on;
36-
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
40+
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml application/wasm;
3741

3842
try_files $file @redmine;
3943
}
4044

45+
4146
# Capture file path after relative url. Can't use rewrite since need
4247
# to pass full path to @redmine proxy.
4348
location ~* {{REDMINE_RELATIVE_URL_ROOT}}(?<file>.*) {

0 commit comments

Comments
 (0)