40

I see the Nginx HttpRewriteModule documentation has an example to rewrite a www-prefixed domain to a non-www-prefixed domain:

if ($host ~* www\.(.*)) {
  set $host_without_www $1;
  rewrite ^(.*)$ http://$host_without_www$1 permanent; # $1 contains '/foo', not 'www.mydomain.com/foo'
}

How can I do the reverse-- rewrite a non-www-prefixed domain to a www-prefixed domain? I thought maybe I could do something like the following but Nginx doesn't like the nested if statement.

if ($host !~* ^www\.) {                       # check if host doesn't start with www.
    if ($host ~* ([a-z0-9]+\.[a-z0-9]+)) {    # check host is of the form xxx.xxx (i.e. no subdomain)
        set $host_with_www www.$1;
        rewrite ^(.*)$ http://$host_with_www$1 permanent;
    }
}

Also I wanted this to work for any domain name without explicitly telling Nginx to rewrite domain1.com -> www.domain1.com, domain2.com -> www.domain2.com, etc. since I have a large number of domains to rewrite.

saltycrane
  • 6,541
  • 6
  • 34
  • 43

7 Answers7

63

As noted in the Nginx documentation, you should avoid using the if directive in Nginx where possible, because as soon as you have an if in your configuration your server needs to evaluate every single request to decide whether to match that if or not.

A better solution would be multiple server directives.

server {
        listen 80;
        server_name website.com;
        return 301 $scheme://www.website.com$request_uri;
}

server {
        listen 80;
        server_name www.website.com;
        ... 
}

If you're trying to serve an SSL (HTTPS) enabled site, you got more or less three different options.

  1. Set up multiple IP addresses having each server directive listening on their own IP (or different ports if that's an option for you). This options needs SSL certificates for both website.com and www.website.com, so either you have a wild card certificate, a UNI certificate (multiple domains) or just plainly two different certificates.
  2. Do the rewrite in the application.
  3. Use the dreaded if directive.

There is also an option to use SNI, but I'm not sure this is fully supported as of now.

dpurrington
  • 1,488
  • 11
  • 20
Lars
  • 731
  • 1
  • 5
  • 4
  • 1
    This solution has the added benefit of sending back an HTTP 301 Moved Permanently response status code. This is an efficient, elegant, and readable solution. Thanks! – ndmeiri Nov 23 '17 at 03:47
16
if ($host !~* ^www\.) {
    rewrite ^(.*)$ http://www.$host$1 permanent;
}
ss.
  • 672
  • 6
  • 5
  • 1
    This doesn't work for me because I don't want to rewrite subdomains like static.mydomain.com. I probably should have stated that in my question. Thanks for your answer. – saltycrane Nov 09 '09 at 18:18
  • Nginx recommends return 301 instead of rewrite, see its common Pitfalls page: https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#taxing-rewrites – kintsukuroi Mar 30 '18 at 22:37
15

Well I guess I don't really need the outer "if" statement since I'm only checking for domains of the form xxx.xxx anyways. The following works for me, though it's not robust. Let me know if there is a better solution.

    if ($host ~* ^([a-z0-9\-]+\.(com|net|org))$) {
        set $host_with_www www.$1;
        rewrite ^(.*)$ http://$host_with_www$1 permanent;
    }

Edit: Added hyphen to the regular expression since it is a valid character in a hostname.

saltycrane
  • 6,541
  • 6
  • 34
  • 43
  • how to use this without if? – pahnin Aug 01 '16 at 10:49
  • Nginx recommends use of return 301 instead of rewrite, see its common Pitfalls page: https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#taxing-rewrites – kintsukuroi Mar 30 '18 at 22:38
7
if ($host ~* ^[^.]+\.[^.]+$) {
    rewrite ^(.*)$ https://www.$host$1 permanent;
}

It's only possible to get valid hostnames because the request will never make it to your server otherwise, so there's no need to build your own validation logic.

Anthony DiSanti
  • 516
  • 8
  • 9
  • Nginx recommends return 301 instead of using rewrite, see its common Pitfalls page: https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#taxing-rewrites – kintsukuroi Mar 30 '18 at 22:39
  • The OP explicitly says that he wants a solution that supports multiple domains without needing to explicitly support each. This is only possible using rewrite. – Anthony DiSanti May 04 '18 at 07:25
  • this wouldn`t work in a scenario where TLDs like something.co.uk and something.com are used. In this case, the only solution seems to be to list the TLDs in use: ^([^.]*\.(net|at|be|bg|de|ch|cy|cz|co\.uk|com|com\.tr|hu|it|es|eu|fr|global|gr|nl|org|pl|pt|se|sk))$ – Sacha Vorbeck Aug 29 '23 at 09:43
4

The nginx documentation cautions against the use of if for rewriting. Please see the link here: http://wiki.nginx.org/Pitfalls#Server_Name

Robert Brown
  • 79
  • 1
  • 3
1

HTTP & HTTPS without if conditions:

server {
    listen          80;
    listen          443;
    server_name     website.com;

    return 301 $scheme://www.website.com$request_uri;
}
server {
    listen          80;
    listen          443 default_server ssl;
    server_name     www.website.com;

    # Your config goes here #
}
Andrei
  • 160
  • 2
  • 14
0

Solution for multiple domains, working on nginx 1.17 for me:

server {
        listen                                80;
        server_name                           .example.com;

        set $host_with_www                 $host;

        if ($host !~* www\.(.*)) {
            set $host_with_www www.$host;
        }

        return                                301 https://$host_with_www$request_uri;
}

In this config example additionally rewrites HTTP on HTTPS, if you don't want rewrite — replace https:// with http:// in return string.

If you want keep protocol — use $scheme variable.

irsi
  • 1
  • 3
  • @NicolasDusart :D Yes, sorry. At begin I want to post another config example, but it's not working properly, so I post this (with if), but don't edit text of comment. – irsi Jul 11 '19 at 18:43
  • @NicolasDusart but at least this example correct for multiple server names. – irsi Jul 11 '19 at 18:44