How nginx's "location if" works (Was Re: Setting cache parameters via if directives)

seekvn nginx-forum at forum.nginx.org
Tue Sep 28 08:57:05 UTC 2021


Hello
Help me convert this .htaccess content

RewriteEngine On
RewriteCond %{HTTP_REFERER} !domain1\.com [NC]
RewriteCond %{HTTP_REFERER} !domain2\.com [NC]
RewriteCond %{HTTP_REFERER} !domain3\.com [NC]
RewriteRule \.rar$ - [F,NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ http://domain1.com/file/$1 [L,R,NC]

Thanks

agentzh Wrote:
-------------------------------------------------------
> On Tue, Feb 1, 2011 at 11:45 PM, Ryan Malayter <malayter at gmail.com>
> wrote:
> >
> > It does in fact work in production on nginx 0.7.6x. Below is my
> actual
> > configuration (trimmed to the essentials and with a few
> substitutions
> > of actual URIs).
> >
> 
> Well, ngx_proxy module's directive inheritance is in action here,
> which gives you nice side effects that you want :)
> 
> I'll analyze some examples here such that people *may* get some light.
> 
> [Case 1]
> 
>     location /proxy {
>         set $a 32;
>         if ($a = 32) {
>             set $a 56;
>         }
>         set $a 76;
>         proxy_pass http://127.0.0.1:$server_port/$a;
>     }
> 
>     location ~ /(\d+) {
>         echo $1;
>     }
> 
> Calling /proxy gives 76 because it works in the following steps:
> 
> 1. Nginx runs all the rewrite phase directives in the order that
> they're in the config file, i.e.,
> 
>         set $a 32;
>         if ($a = 32) {
>             set $a 56;
>         }
>         set $a 76;
> 
>     and $a gets the final value of 76.
> 
> 2. Nginx traps into the "if" inner block because its condition $a = 32
> was met in step 1.
> 
> 3. The inner block does not has any content handler, ngx_proxy
> inherits the content handler (that of ngx_proxy) in the outer scope
> (see src/http/modules/ngx_http_proxy_module.c:2025).
> 
> 4. Also the config specified by proxy_pass also gets inherited by the
> inner "if" block (see src/http/modules/ngx_http_proxy_module.c:2015)
> 
> 5. Request terminates (and the control flow never goes outside of the
> "if" block).
> 
> That is, the proxy_pass directive in the outer scope will never run in
> this example. It is "if" inner block that actually serves you.
> 
> Let's see what happens when we override the inner "if" block's content
> handler with out own:
> 
> [Case 2]
> 
>     location /proxy {
>         set $a 32;
>         if ($a = 32) {
>             set $a 56;
>             echo "a = $a";
>         }
>         set $a 76;
>         proxy_pass http://127.0.0.1:$server_port/$a;
>     }
>     location ~ /(\d+) {
>         echo $1;
>     }
> 
> You will get this while accessing /proxy:
> 
>     a = 76
> 
> Looks counter-intuitive? Oh, well, let's see what's happening this
> time:
> 
> 1. Nginx runs all the rewrite phase directives in the order that
> they're in the config file, i.e.,
> 
>         set $a 32;
>         if ($a = 32) {
>             set $a 56;
>         }
>         set $a 76;
> 
>     and $a gets the final value of 76.
> 
> 2. Nginx traps into the "if" inner block because its condition $a = 32
> was met in step 1.
> 
> 3. The inner block *does* has a content handler specified by "echo",
> then the value of $a (76) gets emitted to the client side.
> 
> 4. Request terminates (and the control flow never goes outside of the
> "if" block), as in Case 1.
> 
> We do have a choice to make Case 2 work as we like:
> 
> [Case 3]
> 
>    location /proxy {
>         set $a 32;
>         if ($a = 32) {
>             set $a 56;
>             break;
> 
>             echo "a = $a";
>         }
>         set $a 76;
>         proxy_pass http://127.0.0.1:$server_port/$a;
>     }
>     location ~ /(\d+) {
>         echo $1;
>     }
> 
> This time, we just add a "break" directive inside the if block. This
> will stop nginx from running the rest ngx_rewrite directives. So we
> get
> 
>     a = 56
> 
> So this time, nginx works this way:
> 
> 1. Nginx runs all the rewrite phase directives in the order that
> they're in the config file, i.e.,
> 
>         set $a 32;
>         if ($a = 32) {
>             set $a 56;
>             break;
>         }
> 
>     and $a gets the final value of 56.
> 
> 2. Nginx traps into the "if" inner block because its condition $a = 32
> was met in step 1.
> 
> 3. The inner block *does* has a content handler specified by "echo",
> then the value of $a (56) gets emitted to the client side.
> 
> 4. Request terminates (and the control flow never goes outside of the
> "if" block), just as in Case 1.
> 
> Okay, you see how ngx_proxy module's config inheritance among nested
> locations take the key role here, and make you *believe* it works the
> way that you want. But other modules (like "echo" mentioned in one of
> my earlier emails) may not inherit content handlers in nested
> locations (in fact, most content handler modules, including upstream
> ones, don't).
> 
> And one must be careful about bad side effects of config inheritance
> of "if" blocks in other cases, consider the following example:
> 
> [Case 5]
> 
>     location /proxy {
>         set $a 32;
>         if ($a = 32) {
>             return 404;
>         }
>         set $a 76;
>         proxy_pass http://127.0.0.1:$server_port/$a;
>         more_set_headers "X-Foo: $a";
>     }
>     location ~ /(\d+) {
>         echo $1;
>     }
> 
> Here, ngx_header_more's "more_set_headers" will also be inherited by
> the implicit location created by the "if" block. So you will get:
> 
>     curl localhost/proxy
>     HTTP/1.1 404 Not Found
>     Server: nginx/0.8.54 (without pool)
>     Date: Mon, 14 Feb 2011 05:24:00 GMT
>     Content-Type: text/html
>     Content-Length: 184
>     Connection: keep-alive
>     X-Foo: 32
> 
> which may or may not what you want :)
> 
> BTW, the "add_header" directive will not emit a "X-Foo" header in this
> case, and it does not mean no directive inheritance happens here, but
> add_header's header filter will skip 404 responses.
> 
> You see, how tricky it is behind the scene! No wonder people keep
> saying "nginx's if is evil".
> 
> Cheers,
> -agentzh
> 
> Disclaimer: There may be other corner cases that I've missed here, and
> other more knowledgeable people can correct me wherever I'm wrong :)
> 
> _______________________________________________
> nginx mailing list
> nginx at nginx.org
> http://nginx.org/mailman/listinfo/nginx

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,174917,292478#msg-292478



More information about the nginx mailing list