I'm setting up small personal file hosting, I made a backend to store uploaded files by their hash directly on the HDD like /f/ab/cd/ef012345678..., but without file extensions and generate an URI like example.com/f/ab/cd/ef012345678.../filename.jpg.

Nginx serves the files right from HDD using this config:

location /f {
     rewrite "^/f/([a-z0-9]+)/([a-z0-9]+)/([a-z0-9]+)(.*)$" /$1/$2/$3 break;
     root /srv/cdn/f;

I.e. it cuts the last part of the URI and reads the file using only it's hash. It works well, but it strips file's mime types to application/octet-stream which forces browser to download all files, but it to display images inlined.

I made a workaround by including in this location a piece of config like this:

if ($request_uri ~* \.png$) {
    add_header Content-Type "image/png";

Which fixes the problem for Google Chrome and for the first load in Firefox.

But when I open the link the second time in Firefox or just reload the page, it prompts me to download it. Apparently, Firefox is handling Content-Type with 304 answer poorly (I checked headers, Nginx sends 304 response and Content-Type header with this configuration if file was pulled from the cache).

I'm looking for way to either determine in Nginx config if the file was server from cache and not add the header of for better Nginx solution.

I know the better idea is to change my backend and add file extensions, it will solve some future problems too, but now I can't sleep and need to know the answer using Nginx only if there is one.

1 Answer 1


I have no idea why this method works and your rewrite...break method does not. But you can capture the regular expression in the location statement and use that in an alias expression, and it seems to leave the Content-Type as per the original request URI.

For example:

location ~ ^/(f/[a-z0-9]+/[a-z0-9]+/[a-z0-9]+) {
    alias /srv/cdn/$1;

See this document for more.

Note that regular expression location statements are ordered, so its position within your configuration file may be significant. See this document for more.

  • Wow, that actually works! Thanks. Makes me want to find why now :D I just assumed it won't work the same way as try_files and rewrite don't - they get mime type by file's extension, not URI.
    – Eirenliel
    Sep 10, 2017 at 17:50
  • 1
    rewrite and try_files changes $uri which is probably why the effective file extension changes. Sep 10, 2017 at 17:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.