When building web applications, we often need to route traffic differently based on client devices. Nginx provides powerful pattern matching capabilities through its $http_user_agent
variable, allowing us to implement sophisticated routing logic.
Here's the improved version of your configuration with better practices:
server {
listen 80;
server_name my_domain.com;
# Mobile device detection
if ($http_user_agent ~* (iPhone|Android)) {
return 301 https://m.domain1.com$request_uri;
}
# Desktop browser detection
if ($http_user_agent ~* (MSIE|Mozilla|Chrome|Safari|Firefox)) {
return 301 https://domain2.com$request_uri;
}
# Default case
return 301 https://domain2.com$request_uri;
}
1. Using return 301
instead of rewrite for permanent redirects
2. Combining patterns with | (OR) operator for cleaner config
3. Adding ~*
for case-insensitive matching
4. Using $request_uri
instead of capturing groups
5. Adding a default case for unmatched user agents
For more complex scenarios, you might want to use map directives:
map $http_user_agent $backend {
default "domain2.com";
~*(iPhone|Android) "m.domain1.com";
~*iPad "domain2.com"; # Treat iPad as desktop
~*Windows\s+Phone "m.domain1.com";
}
1. Complex regex patterns can impact performance
2. Consider using map
for large number of rules
3. User-Agent strings can be spoofed - don't rely on them for security
4. Regularly update your patterns as new devices emerge
You can test using curl with custom User-Agent headers:
curl -A "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)" http://my_domain.com
When building modern web applications, we often need to route traffic differently based on client characteristics. The User-Agent header remains one of the most reliable ways to identify client devices and browsers. Here's a refined approach to handle this in Nginx:
map $http_user_agent $backend {
default "https://domain2.com";
~*iphone "https://m.domain1.com";
~*android "https://m.domain1.com";
~*msie "https://legacy.domain2.com";
~*firefox "https://domain2.com";
}
server {
listen 80;
server_name my_domain.com;
location / {
return 301 $backend$request_uri;
}
}
The map
directive provides several advantages over multiple if
statements:
- Better performance through optimized pattern matching
- Cleaner configuration that's easier to maintain
- More flexible matching with regex capabilities
For more complex scenarios, you can combine multiple conditions:
map $http_user_agent $is_mobile {
default 0;
~*(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino 1;
}
server {
# ... other config ...
if ($is_mobile) {
rewrite ^ https://m.domain1.com$request_uri permanent;
}
}
When implementing User-Agent based routing:
- Place more common user agents first in your map directive
- Consider caching decisions when possible
- Benchmark with realistic traffic patterns
Always verify your routing rules with common testing tools:
curl -A "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X)" http://my_domain.com
curl -A "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko" http://my_domain.com
curl -A "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F)" http://my_domain.com
For more complex scenarios, consider these alternatives:
- Client-side feature detection with JavaScript
- Edge computing solutions like Cloudflare Workers
- Combining User-Agent with other headers like Accept