11
votes

Originally I had a conf like this:

location /some/path/ {
  proxy_pass       http://other.host/foo/;
}

And requests to http://my.domain/some/path/bar would be proxied to http://other.host/foo/bar

I started using variables in the proxy_pass to force nginx to re-resolve DNS:

location /some/path/ {
  resolver        1.2.3.4;
  set $proxy_root  "other.host/foo"
  proxy_pass       http://$proxy_root/;
}

But I found that the remainder of the uri path was no longer being appended, so now requests to http://my.domain/some/path/bar would be proxied to simply http://other.host/foo/.

So I changed it to a regex

location ~ ^/some/path/(.*) {
  resolver        1.2.3.4;
  set $proxy_root  "other.host/foo"
  proxy_pass       http://$proxy_root/$1;
}

But that doesn't include any query parameters, so I updated again

location ~ ^/some/path/(.*) {
  resolver        1.2.3.4;
  set $proxy_root  "other.host/foo"
  proxy_pass       http://$proxy_root/$1?$args;
}

This kinda works, but it means there's a ? in every target address, when only some of the incoming requests actually have a ?query section...

I think I could do some further string manipulation, but this feels like a bit much. Is there a simpler way to proxy_pass as I did originally, but with the proxy target as a variable to force re-resolution?

1
Use $is_args variable instead of hardcoded ?Alexey Ten
I've been fighting with similar problem for days, please have a look here: serverfault.com/questions/638505/… It may help youStugal
I've fixed 'resolover' to 'resolver', just in case somebody cuts and pastes your code :)Andor

1 Answers

5
votes

Instead of working with the location matcher, another option is to use $request_uri and match the part you want to maintain. $request_uri contains the full URI including query parameters (http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_uri).

Since the location block matches /some/path/, use a regular expression to obtain the remainder:

  if ($request_uri ~* "/some/path(/.*$)")
      set  $path_remainder  $1;
  }

Finally, concatenate the remainder:

location /some/path/ {
  resolver        1.2.3.4;
  set $proxy_root  "other.host/foo";
  if ($request_uri ~* "/some/path(/.*$)") {
      set  $path_remainder  $1;
  }
  proxy_pass       http://$proxy_root$path_remainder;
}

As for why this happens, according to http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass :

In some cases, the part of a request URI to be replaced cannot be determined

with one of those cases being

When variables are used in proxy_pass:

location /name/ {
    proxy_pass http://127.0.0.1$request_uri; 
}

In this case, if URI is specified in the directive, it is passed to the server as is, replacing the original request URI.

This is the case here since you have $proxy_root in your proxy_pass directive parameter.