nginx

Why does nginx skip location blocks?


I'm trying to achieve some redirection with location and return but nginx always seems to ignore/skip all of the location blocks and I don't understand why... I need to strip the /CustomContext part from the URL.

server {
    listen 8120 ssl; # legacy HTTPS port
    server_name server.domain.com;

    error_log /var/log/nginx/error.log debug;

    location / {
        return 444;
    }

    location ~ "^/CustomContext/(.*)$" {
        return 301 https://custom.domain.com/$1$is_args$args;
    }
    
    return 444;
}

I'd expect some "test location" entries after "rewrite phase: 1" in the log but there are none:

2025/06/24 11:45:18 [debug] 20#20: *1 http process request line
2025/06/24 11:45:18 [debug] 20#20: *1 http request line: "GET /CustomContext/services/version HTTP/1.1"
2025/06/24 11:45:18 [debug] 20#20: *1 http uri: "/CustomContext/services/version"
2025/06/24 11:45:18 [debug] 20#20: *1 http args: ""
2025/06/24 11:45:18 [debug] 20#20: *1 http exten: ""
2025/06/24 11:45:18 [debug] 20#20: *1 posix_memalign: 00007F47955376D0:4096 @16
2025/06/24 11:45:18 [debug] 20#20: *1 http process request header line
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Host: server.domain.com:8120"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Connection: keep-alive"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Pragma: no-cache"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Cache-Control: no-cache"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "sec-ch-ua: "Microsoft Edge";v="137", "Chromium";v="137", "Not/A)Brand";v="24""
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "sec-ch-ua-mobile: ?0"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "sec-ch-ua-platform: "Windows""
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Upgrade-Insecure-Requests: 1"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Sec-Fetch-Site: none"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Sec-Fetch-Mode: navigate"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Sec-Fetch-User: ?1"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Sec-Fetch-Dest: document"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Accept-Encoding: gzip, deflate, br, zstd"
2025/06/24 11:45:18 [debug] 20#20: *1 http header: "Accept-Language: en-US,en;q=0.9,de;q=0.8,de-DE;q=0.7"
2025/06/24 11:45:18 [debug] 20#20: *1 http alloc large header buffer
2025/06/24 11:45:18 [debug] 20#20: *1 posix_memalign: 00007F4795546020:512 @16
2025/06/24 11:45:18 [debug] 20#20: *1 malloc: 00007F4795664AE0:8192
2025/06/24 11:45:18 [debug] 20#20: *1 http large header alloc: 00007F4795664AE0 8192
2025/06/24 11:45:18 [debug] 20#20: *1 http large header copy: 240
2025/06/24 11:45:18 [debug] 20#20: *1 SSL_read: 517
2025/06/24 11:45:18 [debug] 20#20: *1 SSL_read: -1
2025/06/24 11:45:18 [debug] 20#20: *1 SSL_get_error: 2
[...]
2025/06/24 11:45:18 [debug] 20#20: *1 http header done
2025/06/24 11:45:18 [debug] 20#20: *1 event timer del: 14: 176555899
2025/06/24 11:45:18 [debug] 20#20: *1 generic phase: 0
2025/06/24 11:45:18 [debug] 20#20: *1 rewrite phase: 1
2025/06/24 11:45:18 [debug] 20#20: *1 http finalize request: 444, "/CustomContext/services/version?" a:1, c:1
2025/06/24 11:45:18 [debug] 20#20: *1 http terminate request count:1
2025/06/24 11:45:18 [debug] 20#20: *1 http terminate cleanup count:1 blk:0
2025/06/24 11:45:18 [debug] 20#20: *1 http posted request: "/CustomContext/services/version?"
2025/06/24 11:45:18 [debug] 20#20: *1 http terminate handler count:1
2025/06/24 11:45:18 [debug] 20#20: *1 http request count:1 blk:0
2025/06/24 11:45:18 [debug] 20#20: *1 http close request
2025/06/24 11:45:18 [debug] 20#20: *1 http log handler
2025/06/24 11:45:18 [debug] 20#20: *1 free: 00007F47955352F0, unused: 112
2025/06/24 11:45:18 [debug] 20#20: *1 free: 00007F47955376D0, unused: 2672
2025/06/24 11:45:18 [debug] 20#20: *1 close http connection: 14
2025/06/24 11:45:18 [debug] 20#20: *1 SSL_shutdown: 1
2025/06/24 11:45:18 [debug] 20#20: *1 reusable connection: 0
2025/06/24 11:45:18 [debug] 20#20: *1 free: 00007F4795664

Switching to rewrite statement works:

server {
    listen 8120 ssl; # legacy HTTPS port
    server_name server.domain.com;

    rewrite ^/CustomContext/(.*)$ https://custom.domain.com/$1$is_args$args permanent;

    return 444;
}

Solution

  • Remove the server return

    To fix the described problem, remove the server-block return:

    server {
        ...
    
        location / {
            return 444;
        }
    
        ...
        
        # return 444; <-- delete this
    }
    

    Why

    It's redundant, but also problematic.

    The return directive can be used in a server context - but if it is, it's expected to be used alone as it applies unconditionally to all requests. This explains the observed behavior but also the debug logs - there are no test location debug log messages not because nginx is ignoring the location blocks - but the request ends before reaching the phase to check them.

    Without that server return, requests that don't match any other location block will be processed by the location / block - which acts as the fallback. So the end result matches the intent.