0

I have an nginx reverse proxy location setup like so:

location /192.168.0.10 {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://192.168.0.10/;
}

location /192.168.0.11 {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://192.168.0.11/;
}

location /192.168.0.12 {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://192.168.0.12/;
}

location /192.168.0.13 {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://192.168.0.13/;
}

location /192.168.0.14 {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://192.168.0.14/;
}

location /192.168.0.15 {..... 

This structure repeats about 96 times so that we can have a reverse proxy to each one. Is there a way to simplify it so that there is only one structure and that the IP in the location's path magically just appears in the proxy_pass directive? Because this is becoming a pain to manage as we add and remove servers.

Steven
  • 69
  • 5

1 Answers1

1

I think the following regex matching location should work:

location ~ ^/(?<proxy>192\.168\.0\.(?:10|11|12|13|14|15|...))(?:/(?<path>.*))?$ {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://$proxy/$path;
}

You can additionally optimize that non-capturing group (?:10|11|12|13|14|15|...), for example to match any number from 10 to 105 it can be written as (?:10[0-5]|[1-9]\d).

Update 1

As requested by OP, PCRE regex for any IPv4 address with strict checking (for matching only correct IPv4 addresses from 0.0.0.0 to 255.255.255.255, adapted from this answer) can be written as

location ~ "^/(?<proxy>(?:25[0-5]|2[0-4]\d|[01]?\d?\d\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))(?:/(?<path>.*))?$" {
    ...
}

Due to the curly brackets usage regex pattern must be enclosed in double quotes.

Update 2

Previous solutions won't preserve the request query string, more correct solution to keep all the request query arguments is to use proxy_pass http://$proxy/$path$is_args$args directive:

location ~ "^/(?<proxy>(?:25[0-5]|2[0-4]\d|[01]?\d?\d\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))(?:/(?<path>.*))?$" {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
    proxy_cookie_path /phpmyadmin/ /;
    proxy_redirect off;
    proxy_pass    http://$proxy/$path$is_args$args;
}
Ivan Shatsky
  • 13,267
  • 2
  • 21
  • 37
  • Thanks Ivan. The server numbers is actually not important. The IP range also doesn't have to be dynamic. I struggle to understand the Nginx Regex flavour, so how would it look to make it even more dynamic? To basically catch any IP address. – Steven Oct 12 '21 at 14:48
  • I got it working thanks! I just used the following regex (?[^/]+) for the proxy part. Then it caches anything I put in the first part of the path – Steven Oct 12 '21 at 15:00
  • Yours `[^/]+` will match **any** string at all. See the update for regex example matching only correct IPv4 addresses. – Ivan Shatsky Oct 12 '21 at 15:04
  • Found an error (query string won't be proxied), check an update to the answer. – Ivan Shatsky Oct 12 '21 at 15:27