Even though you’ve configured:
location /media/ {
# ...
if ($request_method = OPTIONS) {
return 204;
}
try_files $uri $uri/ =404;
}
…you also have a regex location for static assets:
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
NGINX’s location matching rules mean that if a regex location matches, it can override a prefix location. For example, GET https://example.com/media/tarot/Queen%20of%20Pentacles.png might end up going through the regex location ~* .(png|...) block. When you send an OPTIONS request to that same URL, it will also match the regex block—unless you explicitly handle OPTIONS there.
Solution:
Add OPTIONS handling inside the regex location as well, or unify your location so that /media/ always handles the file. For example:
nginx
# Make sure that /media/ has precedence by removing or adjusting
# the regex location so it doesn’t override /media/.
# EITHER remove the regex block entirely if you only want to serve
# images from /media/ ...
# OR handle OPTIONS within the regex block, e.g.:
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
# Add CORS headers here too
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-My-Custom-Header" always;
add_header Access-Control-Expose-Headers "Content-Disposition" always;
# Handle preflight
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization, Content-Type";
add_header Access-Control-Expose-Headers "Content-Disposition";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
# Normal static file serving:
try_files $uri =404;
}
Or, an even simpler approach is to remove the separate regex location block if you don’t actually need different caching behavior. Then your /media/ block alone will handle everything.