Nginx User-Agent Based Routing: Redirect Traffic to Different Backends by Device Type


8 views

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