3

I am using nginx for managing multiple domains for reverse proxy.

I want to redirect all http request to https

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

However, I am not sure how can I make it work for rewriting non www requests to redirect to www in generic way excluding rewriting those requests that already has www in requests as if I just modify below rule it creates problem when someone write as www.domain.com it rewrite to www.www.domain.com

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://www.$host$request_uri;
}

Please check this image to understand the issue. redirecting to www.www.domain

So I need to have some condition that handles that.

Krunal
  • 2,967
  • 8
  • 45
  • 101

3 Answers3

1

If you want a generic approach, you can use map directive (should be placed outside the server block):

map $host $basename {
    ~^www\.(.+)  $1;
    default      $host;
}
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$basename$request_uri;
}

Update 1

It's look I misread your question (you need to redirect example.com to www.example.com but my answer does an opposite thing), please try the following:

map $host $basename {
    ~^www\.(.+)  $host;
    default      www.$host;
}
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$basename$request_uri;
}

Update 2

OP asks an additional question:

Is there a way to exclude specific sub domain like static.example.com which I don't want to rewrite to www.static.example.com?

Yes, rewrite of static subdomain can be prevented with the following map block:

map $host $basename {
    ~^(?:www|static)\..+  $host;
    default               www.$host;
}

If you want to prevent rewrite of any three-component domain name, you can use

map $host $basename {
    ~[^.]+\.[^.]+\.[^.]+  $host;
    default               www.$host;
}
Ivan Shatsky
  • 13,267
  • 2
  • 21
  • 37
  • Hi, I have tried with this, however this does not rewrite domain.com to www.domain.com – Krunal Sep 02 '21 at 18:05
  • As I understand your question, you're want an opposite, rewrite www.domain.com to domain.com? – Ivan Shatsky Sep 02 '21 at 19:11
  • thanks, I have tried this. But it seems that http://example.com rewrites to https://www.example.com, but https://example.com fails to rewrites to https://www.example.com. Any idea what can help here? – Krunal Sep 07 '21 at 13:08
  • Also, is there a way to exclude specific sub domain like static.example.com which I don't want to rewrite to www.static.example.com ? – Krunal Sep 07 '21 at 13:11
  • Rewriting `https://example.com` to `https://www.example.com` where `example.com` addresses multiple domains (e.g. `domain1.com`, `domain2.com` etc.) is a more complex task. If you want to do it with the only one `server` block, you'll need a certificate that matches any of those domains. Though it is [possible](https://certbot.eff.org/docs/using.html#:~:text=If%20you%E2%80%99re%20getting%20a%20certificate%20for%20many%20domains%20at%20once), you'll need to list all the domains in the `certbot` command line. Is this acceptable for you? – Ivan Shatsky Sep 07 '21 at 13:59
  • yes, we already do that using certbot command line and has certificate for each domains. – Krunal Sep 07 '21 at 14:04
  • Do you mean you already have **one** certificate that has every of your domain names listed in its SAN (subject alternate name) list like shown [here](https://stackoverflow.com/questions/56553302/add-an-id-in-the-ssl-certificate-subject-field)? Or you have multiply certificates for multiply domains? – Ivan Shatsky Sep 07 '21 at 14:11
  • Answer to your second question given as "Update 2" of the answer. – Ivan Shatsky Sep 07 '21 at 14:19
  • I have multiple certificate. One for each domain. – Krunal Sep 07 '21 at 14:33
  • I see "Update 1" is same where `http://example.com` rewrites to `https://www.example.com`, but `https://example.com` fails to rewrites to `https://www.example.com`. – Krunal Sep 07 '21 at 14:36
  • have you checked it? – Krunal Sep 10 '21 at 06:33
0

Please try this

 server {
        listen 80;
        listen [::]:80;
        server_name yourdomain.com www.yourdomain.com me.yourdomain.com;
        return 301 https://yourdomain.com$request_uri;
       }

this will rewrite all 3 domains to https://yourdomain.com

I am not sure about my answer. I am not able to test this now. please try this and comment back

sojin
  • 2,158
  • 1
  • 10
  • 18
  • I am using multiple domain names, and that prevents me to specify domain in rule. I need something that is generic. Moreover, this rule will not solve the purpose as it is not going to rewrite to www domain. – Krunal Aug 28 '21 at 16:18
  • server_name yourdomain.com www.yourdomain.com me.yourdomain.com; add your multiple domains here and change this "return 301 https://www.yourdomain.com$request_uri;" – sojin Aug 28 '21 at 16:24
  • thanks for your help. what happens is: when visitor enter www.domain.com, it redirects to www.www.domain.com. That's what I need to fix. Moreover, there are more than one domains so need rule that does not require me to write specific domain for each domain entry. – Krunal Aug 31 '21 at 07:12
0

Non-www to www & https redirect

If I understand correctly, then you want to force non www to www. And you want to force all requests to https. This config code checks if the $host start with "www." or not. If it does not, then nginx will add "www." before the $host then return the correct https location. It will work with subdomains as well.

server {
  if ($host !~* ^www\.) {
    # if host name starts with (case insensitive) "www."
    return 301 https://www.$host$request_uri;
  }
  if ($host ~* ^www\. ) {
    # if host name doesn't start with (case insensitive) "www."
    return 301 https://$host$request_uri;
  }
  listen 80 default_server;
  listen [::]:80 default_server;
  server_name _;
  return 404;
}

Possibly better way with Certbot

You can automatically produce the code you need by using Certbot. It will also help you manage certificates a lot and automate renewal. But Certbot will not change non-www to www, you'll have to edit auto-generated config to look like the config above. The other difference is that you will be expected to have different config blocks/files per domain, but not sure if necessary. This is what the default config looks like per domain.

server {
  if ($host = www.yourdomain.com) {
    return 301 https://$host$request_uri;
  } # managed by Certbot


  if ($host = yourdomain.com) {
    return 301 https://$host$request_uri;
  } # managed by Certbot


  listen 80;
  listen [::]:80;
  server_name yourdomain.com www.yourdomain.com;
  return 404; # managed by Certbot
}

I think it's best practice to separate the config files for each domain and subdomain and use the default Certbot format until you have understood Nginx more. This way you can disable the redirect of non-www to www very easily. The default code above from Certbot, would be what you would put when you don't want to redirect in that case. Long term this makes more sense to me, but I understand when users are more comfortable with www or other parts of the website might need the www.

Sam Ulloa
  • 186
  • 2
  • 12
  • shouldn't the second conditional rule have `https://www.$host$request_uri` instead of `https://$host$request_uri`? Also, see `https://example.com` does not redirect to `https://www.example.com`. Pls help – Krunal Sep 15 '21 at 07:56
  • I tested it. The first block works. You can just replace your server config block with that one. `www.` is part of the `$host` so it doesn't need to be added to the return statement. The second code block would need you to fix other parts of the code. Make sure you read everything i wrote. – Sam Ulloa Sep 15 '21 at 15:14
  • Sure. I didn't get what you mean by 'The second code block would need you to fix other parts of the code'? – Krunal Sep 16 '21 at 15:41
  • You can't use the second code block to get your desired outcome. I explained what it does and you should learn more about why it might be better on your own. – Sam Ulloa Sep 16 '21 at 18:07