Nginx redirect all www.domain to domain and all domain to www.domain

nginx

I'm trying to get nginx to redirect every request it receives from any domain by either adding or removing a www. prefix.

This is for a little redirect service. If you point www.any.domain to it, it should redirect www.any.domain/* to any.domain/*. AND if you point any.domain to it, it should redirect any.domain/* to www.any.domain/*.

This won't cause a redirect loop in practise because only one of the two domains (any.domain or www.any.domain) would be pointing to this service. The other to your website on another server.

This should work with any domain. Thousands of different ones, so it has to have a regex wildcard match. The fact that anything on the internet could be pointed to it isn't nginx's concern because that will get handled at a layer above/before it. Every request with an HTTP_HOST header will either have a www or not and in both cases it should be redirected, so the server should redirect all requests it receives.

The case where a domain doesn't start with www. is easy enough, but I can't figure out how to capture the cases where it DOES start with www. and then only use the rest of the domain.

I've tried searching, but a) just about no one redirects both ways at the same time, b) most people only care about one domain, so they hardcode it and c) once you get past that people tend to chant "IF IS EVIL". So that doesn't help much.

This is what I have at the moment:

http {
  server {
    listen 80 default_server;
    server_name _;

    # add www. to URLs that don't start with it.
    if ($host !~* ^www\.) {
      rewrite ^(.*)$ http://www.$host$1 permanent;
    }

    # but what about URLs that DO start with www.? How do I remove it?
  }
}

This is for use with setting up DNS for your site. If you want your site served at mydomain.com, you point mydomain.com to your site and www.mydomain.com to this redirector. If you want your site served at www.mydomain.com, you point www.mydomain.com to your site and mydomain.com to this redirector.

Best Answer

Use two server blocks

That's actually in the docs:

A named regular expression capture can be used later as a variable:

server {
    server_name   ~^(www\.)?(?<domain>.+)$;

    location / {
        root   /sites/$domain;
    }
}

Applied to the use case in the question, the server block required is therefore:

server {
    server_name   ~^(www\.)(?<domain>.+)$;
    return 301 $scheme://$domain$request_uri;
}

Putting it all together

The complete server configs required for the question would be:

server {
    listen 80;
    server_name   ~^(www\.)(?<domain>.+)$;
    return 301 $scheme://$domain$request_uri;
}

server {
    listen 80 default_server;
    return 301 $scheme://www.$host$request_uri;
}

In this way the config is very simple, and there's no usage of if (which generally speaking should be avoided).