nginx

Can nginx passthrough multiple locations for the same request?


I encountered this by accident. There are two location blocks in my nginx config:

location /static/ {
        alias /app/static/;
}

location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
       access_log off; log_not_found off; expires max;
}

The second one was copy-pasted from an older configuration by mistake. The problem I encountered was that my images were not being served, and by turning on nginx debug and looking into it, nginx has matched the second location block for the image file (which is located within the /static tree), and then stopped processing and attempted to load it from the root path - which fails.

So, Is there a way to both have a working /static location alias (or actually multiple such aliases) and have some rules that apply just to certain file extensions?

I can probably move the image matching rule within the /static location, but what if I have dozen of such aliases and I'd like to add some special image rules to all of them? Can I tell nginx to continue looking even if it matches a location rule?


Solution

  • Nginx chooses a single location to process the current request.

    Your current configuration could be improved as follows:

    location ^~ /static/ {
      root /app;
      location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
        access_log off; log_not_found off; expires max;
      }
    }
    
    location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
      access_log off; log_not_found off; expires max;
    }
    

    Use ^~ so the prefix location takes precedence over the regular expression location.

    The root statement should be used instead of alias where possible, as indicated here.

    And, as you suggested, location blocks can be nested.


    If you have many locations with a different root or alias, you could use a include statement to pull the same snippet into multiple points in your configuration.


    Alternatively, both the access_log and expires directives accept a variable, examples are shown here and here.

    So, logic is applied to the original statements in http or server context.

    You would need two maps, both testing $request_uri with a pattern like:

    ~*\.(ogg|ogv|svg...wav|bmp|rtf)(\?|$)
    

    The middle part is skipped to better see the end of the pattern.


    AFAIK, log_not_found does not accept variables.

    This directive controls what Nginx writes to its error log. If you need to suppress 404 responses also generating file not found entries in the error log, use:

    try_files $uri $uri/ =404;
    

    in the respective location blocks, which is similar to the default behaviour, but will suppress writing file not found errors to the error log.