rewrite and map ??interfering regexps

Maxim Dounin mdounin at mdounin.ru
Sun Apr 26 16:28:38 UTC 2020


Hello!

On Sun, Apr 26, 2020 at 01:49:19PM +0100, Norman Gray wrote:

> Greetings.
> 
> I'm trying to do some fairly intricate URI rewriting, and the behaviour of
> the 'rewrite' statement does not correspond to anything I can explain from
> the docs.
> 
> The goal is that /A/foo/modified is rewritten to /_newroot/foo and
> /B/foo/modified to /_defaultroot/foo.  I hope to achieve this with
> 
>     map $uri $modroot {
>         default _defaultroot;
>         ~^/A _newroot;
>     }
> 
> and
> 
>     location ~ /modified$ {
>         rewrite ^(.+)/modified$ /$modroot/-$1-;
>     }
> 
> (in the real config, /_newroot is reverse-proxied to a web service, so that
> the URIs it handles end up grafted on to selected trees; the 'map' is
> intended to select/limit which URIs are passed on to this service).
> 
> The complete nginx.conf is at the bottom.
> 
> Looking in the error log, when I retrieve /hello/modified I find
> 
> 2020/04/26 12:23:28 [notice] 63328#0: *5 "^(.+)/modified$" matches
> "/hello/modified", client: 127.0.0.1, server: localhost, request: "GET
> /hello/modified HTTP/1.1", host: "localhost"
> 2020/04/26 12:23:28 [notice] 63328#0: *5 rewritten data:
> "/_defaultroot/-/hello-", args: "", client: 127.0.0.1, server: localhost,
> request: "GET /hello/modified HTTP/1.1", host: "localhost"
> 
> ...which is fine: the map defines $modroot as /_defaultroot, and the rewrite
> captures the /hello.
> 
> But retrieving /A/hello/modified,
> 
> 2020/04/26 13:00:51 [notice] 63828#0: *6 "^(.+)/modified$" matches
> "/A/hello/modified", client: 127.0.0.1, server: localhost, request: "GET
> /A/hello/modified HTTP/1.1", host: "localhost"
> 2020/04/26 13:00:51 [notice] 63828#0: *6 rewritten data: "/_newroot/--",
> args: "", client: 127.0.0.1, server: localhost, request: "GET
> /A/hello/modified HTTP/1.1", host: "localhost"
> 
> I would expect this to be rewritten to /_newroot/-/A/hello-
> 
> Here, the map has defined $modroot as /_newroot (which is correct).  The
> 'rewrite' _has_ matched, but the $1 in that line appears to be empty.  Note
> the '+' in the regexp: there is supposed be be a string of non-zero length
> in there (ie, this is ruling out that I'm inadvertently matching, and
> replacing, an empty string, as a result of being somehow confused about
> where in the string '^' is matching).
> 
> It's as if the regexp match in the 'map' is somehow interfering with the
> group-capturing in the 'rewrite'.
> 
> As a workaround, I can get this to work with ^(?<newprefix>/A) in the 'map',
> and using $newprefix in the 'rewrite', but that's fiddly/ugly and more
> confusing than localising the rewriting to the 'rewrite' statement.
> 
> Am I misunderstanding how 'rewrite' matches things, or is there an issue
> here?

The issue is that $1..$N variables as used by the second argument 
of the rewrite directive are from the last regular expression 
matched.  And the last regular expression is not the one from the 
first argument of the rewrite directive when using a variable 
from map with regular expressions.  Relevant ticket is here:

https://trac.nginx.org/nginx/ticket/564

Unfortunately, there is no obvious solution.  On the other hand, 
this is something relatively easy to work around.

-- 
Maxim Dounin
http://mdounin.ru/


More information about the nginx mailing list