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


33 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