3

I have a scenario where I want to proxy_pass requests to one of several upstream destinations based on a value which can appear in a header or as a query parameter.

Right now, I've got the header-based mapping mostly figured out:

map $http_x_destination $destination {
    default     upstream0;
    something   upstream1;
    something2  upstream1;
    something3  upstream2;
}
...

server {
    location / {
        proxy_pass https://$destination;
    }
}

Obviously this works well for the cases where we have a header present, but not for those cases where the target is based on an operation. My initial instinct would be to use some sort of conditional logic to see if the header is present, and construct the map based on $http_x_target if it is, or $arg_target instead.

But I've read plenty of exhortations not to use if, as it's considered dangerous, and since I don't know how nginx handles the scoping of definitions in the first place (let alone in if blocks), I'm leery of this approach.

Is there a good way to merge these two sources of information in order to cleanly map their values to upstreams?

a p
  • 3,098
  • 2
  • 24
  • 46
  • You can cascade two maps so that the value of one map is used as the default value of the second. This may work for you. – Richard Smith Jan 09 '20 at 20:56
  • Do you know where the docs or an example for this could be found @RichardSmith ? – a p Jan 09 '20 at 21:10

1 Answers1

2

If you want to map $http_x_target first and $arg_target second, there are a couple of solutions.

You could create a complex string value like "$http_x_target:$arg_target" and use regular expressions to check each side of the separating character.

For example:

map "$http_x_target:$arg_target" $destination {
    default      upstream0;
    ~something   upstream1;
    ~something2  upstream1;
    ~something3  upstream2;
}
...
server {
    location / {
        proxy_pass https://$destination;
    }
}

You could use regular expressions like ~^something: and ~:something$ to test each side of the : in the string.


Or cascade two maps by using the value of the first as the default of the second.

For example:

map $arg_target $arg_destination {
    default     upstream0;
    something   upstream1;
    something2  upstream1;
    something3  upstream2;
}
map $http_x_target $destination {
    default     $arg_destination;
    something   upstream1;
    something2  upstream1;
    something3  upstream2;
}
...
server {
    location / {
        proxy_pass https://$destination;
    }
}

The two maps must control different variable names (e.g. $destination and $arg_destination).

See this document for details.

Richard Smith
  • 45,711
  • 6
  • 82
  • 81