Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nginx rewrite matches, but matched group was missing from rewritten data

Tags:

nginx

I'm trying to do a "different path for different locale" config for nginx but ran into some strange situation here. See my config file here:

map $http_accept_language $locale {
    default         "en-US";
    ~*en            "en-US";
    ~*zh            "zh-CN";
}

server {
    listen 80;
    server_name _;

    location / {
        rewrite_log on;
        rewrite ^/(.*)$ /prerendered/$locale/$1;
    }

    location /prerendered/en-US {
        root /usr/share/nginx/html;
        # try_files $uri $uri/ $uri.html /prerendered/en-US/index.html =404;
    }

    location /prerendered/zh-CN {
        root /usr/share/nginx/html;
        # try_files $uri $uri/ $uri.html /prerendered/zh-CN/index.html =404;
    }
}

So what I want with this config is that based on different locale (default to en-US), nginx should rewrite the url internally to different path and return different files. However, the result I get is this:

file tree:

/usr/share/nginx/html/prerendered
├── en-US                                content:
│   ├── a.html                           a en-US
│   └── index.html                       index en-US
└── zh-CN
    ├── a.html                           a en-US
    └── index.html                       index zh-CN

curl commands:

$ curl http://127.0.0.1/a.html
a en-US
$ curl http://127.0.0.1/a.html -H 'Accept-Language: en'
index en-US
$ curl http://127.0.0.1/a.html -H 'Accept-Language:zh'
index zh-CN

nginx's log:

2020/09/09 09:45:16 [notice] 29#29: *4 "^/(.*)$" matches "/a.html", client: 172.17.0.1, server: _, request: "GET /a.html HTTP/1.1", host: "127.0.0.1"
2020/09/09 09:45:16 [notice] 29#29: *4 rewritten data: "/prerendered/en-US/", args: "", client: 172.17.0.1, server: _, request: "GET /a.html HTTP/1.1", host: "127.0.0.1"

From Nginx's log, the "a.html" was matched, but for some reason missing in the rewritten data if "Accept-Language" header was provided. Am I doing anything wrong here?

like image 505
gwy15 Avatar asked Nov 25 '25 10:11

gwy15


1 Answers

The numeric captures are overwritten by the latest regular expression to be evaluated. The value of $locale is evaluated after the regular expression in the rewrite statement and because the map contains regular expressions, the value of $1 is reset to an empty string.

The usual solution is to use named captures, for example:

rewrite ^/(?<myuri>.*)$ /prerendered/$locale/$myuri last;

You could use the predefined variable $uri instead, but remember that it already contains a leading /, for example:

rewrite ^ /prerendered/$locale$uri last;

However, you do not need to use rewrite, the same function can be achieved with try_files, for example:

root /usr/share/nginx/html;
location / {
    try_files $uri $uri/ /prerendered/$locale$uri =404;
}
like image 113
Richard Smith Avatar answered Nov 28 '25 15:11

Richard Smith