Nginx redirect http://www and naked http/https to https://www
Asked Answered
D

4

10

I would like to redirect all traffic from the following domains:

  • http://domain.com
  • http://www.domain.com
  • https://domain.com

to

  • https://www.domain.com

I have a SSL certificate for the above domain. It hosts a Rails app, served by Passenger.

To accomplish the naked domain redirect, I've set up an URL redirect in my DNSimple account:

URL domain.com  3600  https://www.domain.com

My server blocks are as follows (Inspired by Nginx no-www to www and www to no-www amongst others):

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

    ssl                             on;
    ssl_certificate                 /etc/ssl/domain-ssl.crt;
    ssl_certificate_key             /etc/ssl/domain.key;
    ssl_session_timeout             5m;
    ssl_protocols                   SSLv2 SSLv3 TLSv1;
    ssl_ciphers                     HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers       on;

    server_tokens   off;
    access_log      /dev/null;
    error_log       /dev/null;

    return 301 https://www.domain.com$request_uri;
}

server {
    listen          443 ssl;
    server_name     www.domain.com;

    root                            /home/deploy/app/current/public;
    passenger_enabled               on;
    passenger_app_env               production;
    passenger_set_cgi_param         HTTP_X_FORWARDED_PROTO https;

    ssl                             on;
    ssl_certificate                 /etc/ssl/domain-ssl.crt;
    ssl_certificate_key             /etc/ssl/domain.key;
    ssl_session_timeout             5m;
    ssl_protocols                   SSLv2 SSLv3 TLSv1;
    ssl_ciphers                     HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers       on;
}

passenger_pre_start https://www.domain.com;

What works:

  • naked domain gets redirected to secure https://www
  • http://www domain gets redirected to secure https://www
  • https:// www works

What does't:

  • Naked https:// doesn't work, browsers throws a server not found

Basically I want to redirect all traffic to the secure https://www.domain.com. What am I missing here?

Dail answered 29/3, 2014 at 10:37 Comment(1)
Did you solve this issue with naked domain.com-->https://www.domain.com ?Lampas
M
6

If you don't have a certificate for domain.com redirecting from https://domain.com to https://www.domain.com will not work, because before the browser gets the redirect it has to successfully establish the SSL connection (https is http inside SSL) and this fails because the certificate does not match.

Mornings answered 29/3, 2014 at 11:2 Comment(3)
Thought it was something like that. Should get a wildcard certificate next time. Thanks!Dail
Wildcard will not be enough, e.g. *.domain.com does not match domain.com. So you need to get a certificate which includes at least www.domain.com (or *.domain.com) and also domain.com.Mornings
This is important. If you do not have certs set with wildcard, nothing is gonna workHazelwood
L
5

I had similar kind of scenario and this is how I solved the redirection from

https://domain.com -----> https://www.domain.com

   server {
      listen        443;
       server_name    domain.com;
         if ($host = domain.com) {
        rewrite ^(.*) https://www.domain.com:443$request_uri? permanent;
    }

Hope this helps!

Using if condition in nginx

Directive if has problems when used in location context, in some cases it doesn't do what you expect but something completely different instead. In some cases it even segfaults. It's generally a good idea to avoid it if possible. The only 100% safe things which may be done inside if in location context are: return ...; rewrite ... last;

Lampas answered 2/6, 2014 at 10:44 Comment(2)
Cool! But isn't it expensive to do that check every request?Dail
Directive if has problems when used in location context, in some cases it doesn't do what you expect but something completely different instead. In some cases it even segfaults. It's generally a good idea to avoid it if possible. The only 100% safe things which may be done inside if in location context are: return ...; rewrite ... last;Lampas
T
4

@steffen ulrich correctly writes

If you don't have a certificate for example.com redirecting from https://example.com to https://www.example.com will not work, because before the browser gets the redirect it has to successfully establish the SSL connection (https is http inside SSL) and this fails because the certificate does not match.

So here's an example:

server {
            listen 80;
            listen [::]:80;
            server_name example.com www.example.com;

            return 301 https://www.example.com$request_uri;
    }
   
  server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name example.com;

            return 301 https://www.example.com$request_uri;
   }

server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name www.example.com;

   ====>>> Your site configuratioin Goes here <<======
}
Tamelatameless answered 13/7, 2017 at 11:11 Comment(0)
C
1

Provide a specific server block for the naked domain along with a general default. The more-specific ones will be used first.

    server {
            listen 80;
            listen [::]:80;
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name example.com;

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

            return 301 https://$host$request_uri;
    }
    server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name www.example.com;
            # omitting the rest for https://www.example.com
    }

I use Let's Encrypt for my certificates so something like the following for default_server prevents redirecting the ACME challenges (note the second wildcard server_name for handling all https://*.example.com which don't have their own server block).

    # omit server_name example.com block, same as above
    server {
            listen 80 default_server;
            listen [::]:80 default_server;

            location ~ ^/\.well-known/acme-challenge {
                # LetsEncrypt
                add_header Content-Type text/plain;
                expires 0;
                alias /var/www/html/acme/$host;
                break;
            }
            location ~ ^/(?!\.well-known/acme-challenge) {
                return 301 https://$host$request_uri;
            }
    }
    server {
            listen 443 ssl http2;
            listen [::]:443 ssl http2;
            server_name *.example.com;
            # omitting the rest for https://*.example.com
    }

Setup certificates for the naked example.com, www.example.com and any others:

sudo certbot certonly --manual -d example.com -d www.example.com -d abc.example.com

Chook answered 30/6, 2017 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.