Is the if instruction in 09 location in nginx the devil

This article is an official article on nginx If is Evil... when used in location context Translation and understanding.

1. Introduction

There is a problem with the if instruction in the location block. In some cases, the if instruction will not work as we expected, but may develop in a completely different direction and even cause errors. Therefore, the best way is not to use the if instruction as much as possible.

The 100% safe instructions in the if instruction of the location block are:

Any operation other than the above two instructions may lead to unpredictable effects, or even SIGSEGV errors (memory errors may occur, resulting in abnormal program termination).

It should be noted that the execution results of the if instruction are consistent, that is, for the same two request operations, it is impossible for one to succeed but the other to fail. This means that the if instruction is * "can" * used as long as it has been properly tested and we know enough about the corresponding operation. This suggestion is also applicable to other directives.

In many cases, we cannot avoid using the if instruction. For example, when we need to judge whether a variable is equal to a value or contains a field, we need to use the if instruction:

if ($request_method = POST ) {
  return 405;
}
if ($args ~ post=140){
  rewrite ^ http://example.com/ permanent;
}

2. Alternatives

We can use try_files The instruction, "return..." or "rewrite... last" is used to replace the if instruction. If some conditions permit, you can also move the if instruction to the server block level. At this time, the if instruction can be used safely, because only other Rewrite Module instructions can be used here (the reason will be explained below).

Of course, for some abnormal code response pages or operations that need to use the if instruction to judge the return of 4xx and 5xx, you can also try to use the return instruction with error_page instruction to ensure security. For example, the following example:

location / {
    error_page 418 = @other;
    recursive_error_pages on;

    if ($something) {
        return 418;
    }

    # some configuration
    ...
}

location @other {
    # some other configuration
    ...
}

In addition, in some cases, some embedded script modules (such as lua, embedded perl Or something else ngx third party module )It is also a good idea to complete some more responsible logical operations and judgments.

3. Known error

The following lists some cases where if instructions do not work as expected. Please be careful not to configure these instructions in the production environment.

# Here is collection of unexpectedly buggy configurations to show that
# if inside location is evil.

# only second header will be present in response
# not really bug, just how it works

location /only-one-if {
    set $true 1;

    if ($true) {
        add_header X-First 1;
    }

    if ($true) {
        add_header X-Second 2;
    }

    return 204;
}

# request will be sent to backend without uri changed
# to '/' due to if

location /proxy-pass-uri {
    proxy_pass http://127.0.0.1:8080/;

    set $true 1;

    if ($true) {
        # nothing
    }
}

# try_files wont work due to if

location /if-try-files {
     try_files  /file  @fallback;

     set $true 1;

     if ($true) {
         # nothing
     }
}

# nginx will SIGSEGV

location /crash {

    set $true 1;

    if ($true) {
        # fastcgi_pass here
        fastcgi_pass  127.0.0.1:9000;
    }

    if ($true) {
        # no handler here
    }
}

# alias with captures isn't correcly inherited into implicit nested
# location created by if

location ~* ^/if-and-alias/(?<file>.*) {
    alias /tmp/$file;

    set $true 1;

    if ($true) {
        # nothing
    }
}

If you find something not listed above, you can email to NGINX development mailing list.

4. Cause analysis

The if instruction is essentially a part of the rewrite module. The rewrite module itself is designed to perform operations in an "imperative" manner * * (this is why it is 100% safe to match the return and rewrite instructions in the if) * *. on the other hand. The configuration of nginx is usually declarative, but for some reasons, the user needs to add some non rewrite instructions to the if (for example, use if to judge parameters such as http_origin and then add header). It may not work normally as we mentioned above, although it can work normally in most cases

At present, it seems that the only solution is to completely disable all non rewrite directives in if, but this will cause many existing configurations to fail to work normally (which will greatly affect forward compatibility), so this problem has not been solved.

5. If still used

If you still need to use the if instruction in location, please pay attention to:

  • Explicit if working principle And process
  • Carry out sufficient tests in advance to ensure normal operation

You were warned.

Keywords: Linux Operation & Maintenance network Nginx

Added by irwa82 on Tue, 01 Mar 2022 15:17:22 +0200