Opened 5 years ago

Last modified 17 months ago

#564 accepted defect

map regex matching affects rewrite directive

Reported by: openid.stackexchange.com/user/a91f6b7f-1d1a-4d21-8bf8-89c4300ea338 Owned by:
Priority: minor Milestone:
Component: nginx-core Version:
Keywords: Cc:
uname -a: FreeBSD freebsd 10.0-RELEASE FreeBSD 10.0-RELEASE #0 r260789: Thu Jan 16 22:34:59 UTC 2014 root@snap.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64
nginx -V: nginx version: nginx/1.6.0 TLS SNI support enabled configure arguments: --prefix=/usr/local/etc/nginx --with-cc-opt='-I /usr/local/include' --with-ld-opt='-L /usr/local/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx-error.log --user=www --group=www --http-client-body-temp-path=/var/tmp/nginx/client_body_temp --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp --http-proxy-temp-path=/var/tmp/nginx/proxy_temp --http-scgi-temp-path=/var/tmp/nginx/scgi_temp --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp --http-log-path=/var/log/nginx-access.log --with-http_stub_status_module --with-pcre --with-http_ssl_module

Description

Using a regex in the map directive changes the capture groups in a rewrite directive. This happens only if the regex in map is matched. A minimal exampe config:

http {
        map $http_accept_language $lang {
                default en;
                 ~(de) de;
        }
        server {
                server_name test.local
                listen 80;
                rewrite ^/(.*)$ http://example.com/$lang/$1 permanent;
        }
}

Expected:

$ curl -sI http://test.local/foo | grep Location
Location: http://example.com/en/foo
$ curl -H "Accept-Language: de" -sI http://test.local/foo | grep Location
Location: http://example.com/de/foo

Actual:

$ curl -sI http://test.local/foo | grep Location
Location: http://example.com/en/foo
$ curl -H "Accept-Language: de" -sI http://test.local/foo | grep Location
Location: http://example.com/de/de

If I leave out the parentheses in ~(de) de; (so it becomes ~de de;), $1 is simply empty:

$ curl -H "Accept-Language: de" -sI http://test.local/foo | grep Location
Location: http://example.com/de/

Change History (9)

comment:1 Changed 5 years ago by www.google.com/accounts/o8/id?id=AItOawkF-Wzs5R74gRwiuGIX3tEfQd-ovqov_P0

Per the docs, this seems to be by design:

http://nginx.org/en/docs/http/ngx_http_map_module.html

"A regular expression can contain named and positional captures that can later be used in other directives along with the resulting variable."

Though it looks like in practice the docs should make it clear that if you use a regex-based map variable it's going to take over the regex context of directives.

Last edited 5 years ago by www.google.com/accounts/o8/id?id=AItOawkF-Wzs5R74gRwiuGIX3tEfQd-ovqov_P0 (previous) (diff)

comment:2 Changed 5 years ago by mdounin

  • Status changed from new to accepted

This case clearly shows that the current behaviour is bad, and should be fixed. While positional captures are clearly bad in most cases, but just a rewrite should be simple enough for them to work.

comment:3 Changed 3 years ago by claudeha@…

It would be more useful IMO if the positional captures could be used within the map directive itself. Here's a silly example that could be achieved with other things, but just to show what I mean:

map $http_accept_language $myindex {
    default index.en.html;
    ~(.*) index.$1.html;
}

I ran into a case recently where it would have been very useful to have some regex with different numbers of matches replaced within a map directive http://serverfault.com/questions/769373/porting-rewritecond-query-string-from-apache2-to-nginx

Last edited 3 years ago by claudeha@… (previous) (diff)

comment:4 Changed 3 years ago by mdounin

See also #1044.

comment:5 Changed 3 years ago by mdounin

See also #1142.

comment:6 Changed 2 years ago by stackoverflow.com/users/1100117/higuita

the nginx docs should be clear that named captures should always be used to avoid problems. By not saying that, users will use positional captures until something breaks and wasting lots of time debugging a simple badly documented feature.

Simply point to named captures in the docs, give one example and everyone will start to use then. without it, people fallback to the simplest capture and fail at random times

comment:7 Changed 2 years ago by mdounin

See also #1285.

comment:8 Changed 23 months ago by https://stackoverflow.com/users/1100117/higuita

can someone change the component to documentation, as this is mostly a documentation bug

thanks

comment:9 Changed 17 months ago by mdounin

See also #1498.

Note: See TracTickets for help on using tickets.