28

Here is my current nginx conf:

server {
  listen 90;
  server_name www.domain.com www.domain2.com;
  root /root/app;
  location / {
    try_files $uri =404;
  }
  location ~ /([-\w]+)/(\w+)/ {
    proxy_pass bla bla
  }
}

it works fine, both www.domain.com and www.domain2.com serve the same content.

now I'd like to add

if the user is visiting www.domain.com and the user agent is xxx then redirect to www.domain2.com

I've searched and tried a lot of method but none of them works.

3
  • Do you still want to serve the same content, even after the redirect? May 8, 2016 at 9:37
  • @Pothi yes, exactly
    – wong2
    May 8, 2016 at 9:38
  • Ok. Please check my answer. May 8, 2016 at 12:15

3 Answers 3

19

There are Two ways to fix this issue.

  1. Have two seperate "server" blocks for www.domain.com & www.domain2.com and add the following lines of rules to "server" block www.domain.com. This is recommended way to solve this issue.

    if ($http_user_agent ~* "^xxx$") {
       rewrite ^/(.*)$ http://www.domain2.com/$1 permanent;
    }
    
  2. If you want to manage the redirect with a single "server" block for both domains, Try below rules

    set $check 0;
    if ($http_user_agent ~* "^xxx$") {
        set $check 1;
    }
    if ($host ~* ^www.domain.com$) {
        set $check "${check}1";
    }
    if ($check = 11) {
        rewrite ^/(.*)$ http://www.domain2.com/$1 permanent;
    }
    
1
8

Step 1: Have two server blocks, one each for domain.com and domain2.com.

Step 2: Use if correctly as it is evil if used incorrectly.

Here's the complete solution...

server {
  listen 90;
  server_name www.domain.com;
  root /root/app;

  # redirect if 'xxx' is found on the user-agent string
  if ( $http_user_agent ~ 'xxx' ) {
    return 301 http://www.domain2.com$request_uri;
  }

  location / {
    try_files $uri =404;
  }
  location ~ /([-\w]+)/(\w+)/ {
    proxy_pass bla bla
  }
}

server {
  listen 90;
  server_name www.domain2.com;
  root /root/app;
  location / {
    try_files $uri =404;
  }
  location ~ /([-\w]+)/(\w+)/ {
    proxy_pass bla bla
  }
}
3
  • Instead of 301, you may use 302 too, depending on your use-case. May 8, 2016 at 12:17
  • hmm, I think this solution contains too many duplicating codes
    – wong2
    May 8, 2016 at 14:11
  • There are multiple ways to solve an issue. I posted my solution only to make you to see the logic behind how it could be solved. There are multiple ways to avoid duplicates. May 9, 2016 at 1:53
6

The recommended way would probably be using a map, also because these variables are evaluated only when they are used.

Also the use of return 301 ... is preferred over rewrites, because no regular expression have to be compiled.

Here an example of where host and user-agent as a concatenated string are compared to a single regex:

map "$host:$http_user_agent" $my_domain_map_host {
  default                      0;
  "~*^www.domain.com:Agent.*$" 1;
}

server {
  if ($my_domain_map_host) {
    return 302 http://www.domain2.com$request_uri;
  }
}

And this could be even more flexible, for example if a there not 2 but more domains involved.

Here we map www.domain.com with user-agents starting with Agent to http://www.domain2.com and www.domain2.com with the exact user-agent Other Agent to http://www.domain3.com:

map "$host:$http_user_agent" $my_domain_map_host {
  default                             0;
  "~*^www.domain.com:Agent.*$"        http://www.domain2.com;
  "~*^www.domain2.com:Other Agent$"   http://www.domain3.com;
}

server {
  if ($my_domain_map_host) {
    return 302 $my_domain_map_host$request_uri;
  }
}

NB you will need nginx 0.9.0 or higher for the concatenated string in map to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.