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.