We sometimes write.

Of course we cannot always share details about our work with customers, but nevertheless it is nice to show our achievements and share some solutions.

Nginx: Proper way to use different reverse proxy upstream based on user-agent without using if

Published on February 24th 2020 - see original post


Using if in Nginx's configs is considered bad, even called evil (see if is evil). Although a lot of Nginx configs exist where conditions are set using if (and they mostly work, too!), it is encouraged to use another approach.

In this particular scenario, traffic being identified from a certain user-agent should be using a different backend/upstream than the default traffic. An if rule would have been very easy:

server {
[...]
    if ($http_user_agent ~ mybot ) {
        proxy_pass http://127.0.0.1:8090;
    }

    proxy_pass http://127.0.0.1:8080;
[...]
}

But once again: We don't want to use if!

map to the rescue!

In the past an almost similar solution had to be achieved; see article different proxy_pass upstream depending on client ip address in Nginx. The difference? A client IP address or range should use a "geo" map (to be able to use ranges). User agents are strings therefore map can be used without a problem.

The map should be defined before the server context (definitely not within):

map "$http_user_agent" $targetupstream {
  default        http://127.0.0.1:8080;
  "~^mybot"      http://127.0.0.1:8090;
}

What this does is:

Dynamic proxy_pass

The map definition above means that the upstream is now stored in a variable $targetupstream. This means: The upstream URL is now dynamic! Nginx will do the mapping according to (here) the request's user-agent. All that needs to be done is to use the variable as proxy_pass:

# HTTPS track
server {
[...]
  location / {
    include /etc/nginx/proxy.conf;
    proxy_set_header X-Forwarded-Proto https;
    proxy_pass $targetupstream;
  }
[...]
}

Pretty awesome, right? Oh, and guess what? We didn't even use if! =)