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!
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:
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! =)
Samuel from wrote on Mar 1st, 2021:
Hello Claudio,
thank you for your kind answer!
proxy1 & 2 are defined above in the file as upstreams like so :
upstream proxy1 {
server localhost:3009;
}
i checked the variables by sending them in a debug header like so :
add_header X-debug-message "$targetupstream" always;
which gives me the proper variable, so the map & regex are working as intended, but something down the road isn't..
La nuit porte conseil as they say, maybe i'll have more luck tomorrow!
thanks again,
samuel
ck from Switzerland wrote on Mar 1st, 2021:
Hi Samuel. The include I am using (proxy.conf) just contains a couple of proxy settings, such as proxy_set_header X-Forwarded-Host and others. This include is not relevant to using this map.
Your config seems to look alright, I cannot spot any issue with it. Except maybe the definition of "proxy1" and "proxy2" somewhere but if you use DNS or IP addresses this should work.
Samuel from wrote on Mar 1st, 2021:
hi! i'm trying to replicate on my own server but can't get it to work. Could you please give me a hint on what i'm doing wrong?
map "$http_user_agent" $targetupstream {
default http://proxy1;
"~^Mozilla" http://proxy2;
}
server {
listen 80;
server_name xxx.xx www.xxx.xx;
return 301 https://xxx.xx;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name xxx.xx www.xxx.xx;
location / {
proxy_set_header X-Forwarded-Proto https;
proxy_pass $targetupstream;
}
[...]
}
nginx runs without issue but just redirects me to the default root seemingly without executing the /location block.
I'm ommiting the "include" line of your code because i have all my code in the same file (sites-enabled/default). Could that be the issue?
thank you!
AWS Android Ansible Apache Apple Atlassian BSD Backup Bash Bluecoat CMS Chef Cloud Coding Consul Containers CouchDB DB DNS Database Databases Docker ELK Elasticsearch Filebeat FreeBSD Galera Git GlusterFS Grafana Graphics HAProxy HTML Hacks Hardware Icinga Influx Internet Java KVM Kibana Kodi Kubernetes LVM LXC Linux Logstash Mac Macintosh Mail MariaDB Minio MongoDB Monitoring Multimedia MySQL NFS Nagios Network Nginx OSSEC OTRS Office OpenSearch PGSQL PHP Perl Personal PostgreSQL Postgres PowerDNS Proxmox Proxy Python Rancher Rant Redis Roundcube SSL Samba Seafile Security Shell SmartOS Solaris Surveillance Systemd TLS Tomcat Ubuntu Unix VMWare VMware Varnish Virtualization Windows Wireless Wordpress Wyse ZFS Zoneminder