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