First of all, your ^/(123[0-9])$
regex will match only /1234
URI (or /1230
, /1231
, etc.) but not the /1234/some/path
since you are using the $
end-of-string anchor. I'm assuming it isn't an error but a design solution. If it isn't, to match both /1234
and /1234/some/path
(but not something like /12345
), you can use an alternation: ^/(123[0-9])(?:/|$)
(I'm using a non-capture group (?:...)
here, as it is considered to have slightly better performance than the capture group).
Your configuration have a whole bunch of various mistakes. Lets look over each one.
Mistake #1: Incorrect rewrite ... last;
usage.
Indeed, you can't specify an URI for the proxy_pass
upstream like you are trying to do with a trailing slash
proxy_pass http://localhost:$1/; # add slash not allow
inside regex matching locations (as well as inside named locations) and the only way to change the URI that will be passed to the backend is to use the rewrite
directive. However the right way to change an URI inside the location
block to process it later within the same location is to use rewrite ... break;
since rewrite ... last;
will force nginx to search a new location for the rewritten URI. That means we need to use the break
flag for the rewrite
directive:
rewrite ^/[0-9]{4}(.*)$ $1 break;
Beware! No directives from the ngx_http_rewrite_module
will be executed after rewrite ... break;
(or simply break;
) directive (update: after some testing, in opposite to what is said in the documentation, turns out it is true only for break
directive but not the rewrite ... break
one, at least for the OpenResty 1.17.8.2 based on nginx 17.8 core).
Mistake #2: You should enquote any string containing curly brackets or they will be considered as nginx configuration block.
Previous directive will give us the following error:
nginx: [emerg] directive "rewrite" is not terminated by ";"
To get rid of this error we need to enquote our regex pattern due to the curly brackets usage:
rewrite "^/[0-9]{4}(.*)$" $1 break;
Mistake #3: Numbered captures are overwritten whenever regular expression is evaluated.
Lets assume you have an URI of /1234
:
location ~ ^/(123[0-9])$ {
# Here the value of '$1' variable is "1234"
rewrite "^/[0-9]{4}(.*)$" $1 break;
# Here the value of '$1' variable is an empty string!
proxy_pass http://localhost:$1; # There will be no port for 'proxy_pass' directive
}
You can save that $1
value before the rewrite rule evaluates its own regular expression using set
directive:
location ~ ^/(123[0-9])$ {
set $port $1;
rewrite "^/[0-9]{4}(.*)$" $1 break;
proxy_pass http://localhost:$port;
}
or better use the named capture group:
location ~ ^/(?<port>123[0-9])$ {
rewrite "^/[0-9]{4}(.*)$" $1 break;
proxy_pass http://localhost:$port;
}
Sometimes there can be no other solution but to use named captures, an excellent example of such a situation is given at this thread.
Mistake #4: Rewritten URI can't be an empty string.
Again, lets assume you have an URI of /1234
. After the rewrite rule an internal nginx $uri
variable will have an empty value. That would lead to HTTP 500 Internal Server error, and in nginx error log you'll see the following error message:
the rewritten URI has a zero length
To resolve that error just rewrite any URI to the /
:
location ~ ^/(?<port>123[0-9])$ {
rewrite ^ / break;
proxy_pass http://localhost:$port;
}
If you'd need to serve any /<port_number>/some/path
request like above, you should use the following location block to ensure your rewritten URI starts with a slash:
location ~ ^/(?<port>123[0-9])(?:/|$) {
rewrite "^/\d{4}(?:/(.*))?" /$1 break;
proxy_pass http://localhost:$port;
}
Mistake #5: You need to specify a resolver when using variables with the proxy_pass
directive.
The above configuration is close to the working solution. However it is still non-workable, giving you HTTP 502 Bad Gateway error. You need to specify a resolver
when you are using variables with the proxy_pass
directive and your upstream is specified by the domain name rather than IP address, otherwise you'll get the following error in your nginx error log:
no resolver defined to resolve localhost
Generally you'd have to use something like
resolver 8.8.8.8;
location ~ ^/(?<port>123[0-9])$ {
rewrite ^ / break;
proxy_pass http://localhost:$port;
}
but since your upstream is localhost
, you can specify its IP address instead of the domain name, this way you won't need that resolver
directive at all:
location ~ ^/(?<port>123[0-9])$ {
rewrite ^ / break;
proxy_pass http://127.0.0.1:$port;
}
This configuration should be fully workable (at last). And once again, if you'd need to serve any /<port_number>/some/path
request like above, you should use the following location block instead:
location ~ ^/(?<port>123[0-9])(?:/|$) {
rewrite "^/\d{4}(?:/(.*))?" /$1 break;
proxy_pass http://127.0.0.1:$port;
}