Search code examples
nginxnginx-reverse-proxynginx-config

nginx `try_files` do not return the right file


worker_processes auto;

events {
    worker_connections 1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    # Map to determine the preferred image format
    map $http_accept $image_suffix {
        default "";
        "~*image/avif" ".avif";
        "~*image/webp" ".webp";
    }

    server {
        listen 80;

        location /public/images/ {
            root /usr/share/nginx/html;

            # Extract the base name without extension
            set $base_uri $uri;
            if ($uri ~* ^(.+)\.(jpeg|jpg|png)$) {
                set $base_uri $1;
            }

            add_header X-debug-image $base_uri$image_suffix;
            add_header X-debug $uri;
            add_header Vary Accept;

            # Try serving the preferred format or fallback to the original
            try_files $base_uri$image_suffix $uri =404;

        }
    }
}
# pwd
/usr/share/nginx/html
# ls -l public/images
bkw2p3rvoe.avif
bkw2p3rvoe.jpeg
bkw2p3rvoe.webp

When I request my image http://localhost:8181/public/images/bkw2p3rvoe.jpeg in the browser, it returns the jpeg, but it should return the .avif:

Request headers:

accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7```

Response headers:

content-type: image/jpeg
vary: Accept
x-debug: /public/images/bkw2p3rvoe.jpeg
x-debug-image: /public/images/bkw2p3rvoe.avif

Solution

  • You cannot use if in a location like that. There used to be a page explaining this, but it's disappeared.

    You could use a regular expression location instead.

    For example:

    location /public/images/ {
        root /usr/share/nginx/html;
    
        location ~* ^(?<base_uri>.+)\.(jpeg|jpg|png)$ {
    
            add_header X-debug-image $base_uri$image_suffix;
            add_header X-debug $uri;
            add_header Vary Accept;
    
            try_files $base_uri$image_suffix $uri =404;
        }
    }
    

    Use a named capture, as numeric captures are reset whenever Nginx evaluates another regular expression. Because your map contains a regular expression and is evaluated after the location (where $image_suffix is first used), $1 will be empty.

    All three add_header statements must appear within the same block, and need to be inside the regular expression location to have access to the capture.