📜 ⬆️ ⬇️

Nginx configuration errors (or how to write rewrites correctly)

Hi, habra people!

On duty, you have to work with web developers, who sometimes write their scripts with rewrites, which they have to adapt to nginx. I have to rake what is written there.

Anyone wishing to receive help on rewriting may ask questions in the comments, then, probably, one more post will be issued from this.
')

Error number 1, the most fatal.


A huge number of times mentioned in the list. Namely, the use of if at the location level.
The problem is that if in a location are not arranged as we imagine. We think that a request comes in, a condition is checked, if it is true, corrections to the configuration are made. In fact, everything is completely different. When launched, nginx generates separate configurations of locations for true and false conditions in if. Some terrible examples:
 location / { set $true1 1; set $true2 1; if($true1) { proxy_pass http://127.0.0.1:8080; } if($true2) { set $expr 123; } } 

Segmentation fault in the workflow when trying to find an upstream for proxy_pass from the first if'a. And the thing is that he did not inherit the location, where both conditions are correct.

 location / { set $true 1; try_files /maintenance.html $uri @fallback; if($true) { set $expr 123; } } location @fallback { proxy_pass 127.0.0.1:8080; } 

Completely ignore try_files. It is just not in the location, which turned out when the expression is true.

By “set $ expr 123;” you can understand almost any expression in if that does not specify a handler for the request — all set, limit_rate, and so on.

However, in one case it is still possible to use if in a location - if immediately after if'a we leave this location. This can be done in two ways:
1) Via rewrite ... last;
 location / { try_files /maintenance.html $uri @fallback; if($cookie_uid = '1') { rewrite ^ /user/panel last; } } location /user { proxy_pass 127.0.0.1:8080; } 

2) Through the return ...;
 location / <c try_files /maintenance.html $uri @fallback; if($cookie_bot = '1') { return 403; } } 

In this case, we can return both to complete the processing of the request, and go to another location, via error_page.
By the way, if at the server level acts exactly as we expect. When using it, global problems should not arise.

Errors number 2, less fatal.


After apache and its htaccess with RewriteRule and RewriteCond, I really want to push everything into one location with if'ami, which will divert processing to another place. But it is ugly, wrong and ineffective.
Error number 2.1, about if (-e ...)

Especially in order to write such rewrites beautifully, a special directive was invented - try_files. In its simplest form, it is usually written as:
try_files $uri $uri/ @fallback
which means:
1. Check if the requested file exists. If yes, give it away; if not, go further.
2. Check if the directory with the requested name exists. If yes, give it away; if not, go further.
3. Submit the processing request to the named location @fallback.

Attention! In @fallback, when using fastcgi, you should not do rewrite. Just write fastcgi_parm with the required script. So a design like
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php

Turns into
 root /path/to/root; location / { try_files $uri $uri/ @fallback; } location @fallback { fastcgi_pass backend; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/index.php; # , ,     S_C_R_I_P_T,   -  ! } 

In the case of using Beckend not FastCGI, but HTTP, you can write it like this:
 root /path/to/root; location / { try_files $uri $uri/ /index.php; } 

You need to understand that all try_files arguments, except the last one, will be perceived as simple files for feedback, and the last argument is already a new target (that is, you can write a processing uri or named location there). Therefore, an attempt to write try_files $ uri $ uri / /index_new.php /index_old.php will not lead to anything good - instead of executing index_new.php, its contents will be given.

With the help of try_files you can do another amazing thing - without editing configs, close the entire site for maintenance. This is done by writing try_files /maintenance.html $ uri $ uri / @fallback; and then simply creating / moving the file /maintenance.html with a message about the technical work. This will not give the expected drop in performance due to the use of open_file_cache, which also caches failed attempts to open the file.

Error number 2.2, about if ($ uri ~)

So, there is a fairly simple rule - if your configuration has a line beginning with “if ($ uri ~", then nginx is configured incorrectly. All the logic of checking uri for something should be implemented through the location, which now support and allocations.
RewriteCond %{REQUEST_URI} ^/rss[0-9]{1-6}
RewriteRule ^(.*)$ rss.php

goes into
 location ~ /rss[0-9]{1-6} { fastcgi_pass backend; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/rss.php; # , ,     S_C_R_I_P_T,   -  ! } 


Error number 2.3, about if ($ host ~)

Everything said for if ($ uri ~) is also true for if ($ host ~). To use these rules, simply create a server with server_name in the form of a regexp!

Not a mistake, but a rake with secretions

If you dare to rewrite the config using selections in server / location, then you will surely come across a small problem. It lies in the fact that when entering the location, the server allocations are erased, when the if is entered or when rewrite is used, the location allocations are erased. Alas, I’m not too familiar with apache rewrites to provide the code, so I’ll describe what I need.
abc.mysite.com/xyz.php mysite.com/abc/index.php?p=xyz
In order for this to work, you have to write like this:
 server { server_name ~ ^(.*)\.mysite\.com$; set $subdom $1; location ~ \/(.*)\.php { set $script $1; rewrite ^ http://mysite.com/$subdom/index.php?p=$script; } } 

In future versions of nginx, it is planned to make separate prefixes for allocations at each level (for example, the rewriting described above can be described as
  rewrite ^ http://mysite.com/$sc_1/index.php?p=$lc_1; 
), but so far this is not, so you have to write like that.

Another good practice is to give meaningful names to variables :-).

Conclusion


Thanks to everyone who read the post to the end. It turned out to be quite long, but I wanted to describe everything as clean and clear as possible.
Of course, there are other configuration errors — buffers, paths, but this is the concern of the system administrator, not the webmaster.

Used materials:
Russian nginx mailing list
Nginx documentation

Source: https://habr.com/ru/post/74135/


All Articles