Site icon GetPageSpeed

NGINX locations: performance impact and optimizations

nginx

nginx

The location directive in NGINX can be considered as a core one as it’s the most crucial to have a functional web server.

Without further ado, let’s review on best performance practices in regards to it.

Best practice #1. Speed up regular expressions in location

Using regular expression in location directive means evaluating/compiling them at runtime, by default.
However, you can greatly improve the performance of matching to those locations, by precompiling those regular expressions on NGINX startup.

The magic directive here is pcre_jit on;. Place it in the main context (e.g. atop your nginx.conf file).

From the official documentation:

PCRE JIT can speed up the processing of regular expressions significantly.

Best practice #2. Use exact matching, where applicable

&tldr – replace regular expression matching with exact matching, where possible

Even though you can greatly improve the performance of locations defined by regular expression, they still result in some performance penalty.

Especially so, if there are many of them because NGINX has to evaluate each of them, sequentially.

location ~ ^/foo/bar/?$ {rewrite ^(.*)$ /foo permanent;}
location ~ ^/lorem/ipsum/?$ {rewrite ^(.*)$ /lorem permanent;}

Having a few rules like this is not a big problem. But if you’re rewriting URL structure for many URLs (think thousands),
the best approach would be using exact matching:

location = /foo/bar/ { return 301 /foo; }
location = /foo/bar { return 301 /foo; }
location = /lorem/ipsum/ { return 301 /lorem; }
location = /lorem/ipsum { return 301 /lorem; }

Best practice #3. Isolate regular expressions in a nested location

&tldr; Use nested locations instead of rewrite

The creator of NGINX, Igor Sysoev, at a conference in 2014, had to mention that NGINX evolved over time with nested locations.
The rewrite directive was a predecessor to nested locations, and thus should be avoided.

I haven’t said anything about rewrite‘s because you shouldn’t be using them at all

Using nested locations allows us to isolate the regular expressions.

Isolating regular expression location under prefixed locations helps to avoid regex matching for each request!

Let’s take the following example:

server {
    rewrite ^/img/(.+)$ /index.php?img=$1 last;

    location / {
        try_files $uri /index.php;
    }

    location ~ \.php$ {
        fastcgi_pass ...
        include fastcgi_params;
    }
}

Why it’s bad? For any request, the regular expression ^/img/(.+)$ is evaluated. Surely it is fast in our example.
But in case you have anything slightly more complex than that, you absolutely want to avoid evaluating it for any request.

How to make the same rewrite, using nested location:

server {
    location /img/ {
        location ~ ^/img/(?<img>.+)$ {
            fastcgi_pass ...
            fastcgi_param SCRIPT_FILENAME /path/to/index.php;
            fastcgi_param QUERY_STRING img=$img;
            include fastcgi_params;
        }
    } 

    location / {
        try_files $uri /index.php;
    }

    location ~ \.php$ {
        fastcgi_pass ...
        include fastcgi_params;
    }
}

How is one better than the other? For a request to /foo, NGINX will not do any regular expression evaluation.
It will only do prefix matching, choose the location /, and ultimate land in location ~ \.php via try_files.

As you see from the above, using nested locations often means having to duplicate some NGINX configuration directives.

Fear not. The author of NGINX recommends the principle of copy-pasting/duplication in NGINX configuration.
According to Igor, this actually saves time in the long run, when you have to untangle one mighty regex location or rewrite into smaller pieces, in order to apply a more specific configuration to a set of URIs.

Exit mobile version