Max
Max

Reputation: 841

Nginx: redirect non-www to www on https

Nginx isn't working to redirect non-www to www if I'm on https:

https://domain.com to https://www.domain.com

My curent setup in .conf is:

server {
  listen 80;
  server_name www.domain.com domain.com;
  return 301 https://www.domain.com$request_uri;
}
server {
  listen 443;
  server_name domain.com;
  return 301 https://www.domain.com$request_uri;
}
server {
  listen IP_ADDRESS:443 ssl;
  server_name www.domain.com;
  ...
}

http://domain.com to https://www.domain.com and http://www.domain to https://www.domain.com works, but non-www to www on https isn't working.

If I added the IP_ADDRESS on the second server block, I get an error in Chrome (SSL error) and both (www and non-www) stop working.

UPDATE:

Thanks to Steffen (below answer), I updated the self-signed certificate to be *.domain.com and not domain.com.

The .conf file was updated under this format:

ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;

server {
  listen 80;
  server_name www.domain.com domain.com;
  return 301 https://www.domain.com$request_uri;
}
server {
  listen 443 ssl;
  server_name domain.com;
  return 301 https://www.domain.com$request_uri;
}
server {
  listen 443 ssl;
  server_name www.domain.com;
  ...
}

Upvotes: 3

Views: 7223

Answers (5)

nsgulliver
nsgulliver

Reputation: 12671

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

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

 server {
      listen        443;
       server_name    example.com;
         if ($host = example.com) {
        rewrite ^(.*) https://www.example.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;

Upvotes: 9

Yogesh Trivedi
Yogesh Trivedi

Reputation: 149

I have used rewrite on both server directive and it worked for me:

General Rewrite Directive non www to https wwww

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

SSL Rule directive for non www to https wwww

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

Upvotes: 0

Boo
Boo

Reputation: 51

In 2nd server block (one starting with "listen 443;") you must add all SSL-related directives that are in the SSL server group (last group). This is my example.conf:

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

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

    # SSL
    ssl on;
    ssl_certificate /var/www/example.com/cert/bundle.cer;
    ssl_certificate_key /var/www/example.com/cert/example.com.key;

    # Enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # Intermediate cypersuite as recommended by Mozilla
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;
    # Add HSTS (HTTPStrictTransportSecurity)
    add_header Strict-Transport-Security "max-age=31536000";
}


server {
    listen 443 ssl;
    server_name www.example.com;
    root /var/www/example.com/public;
    index index.html index.htm index.php;
    client_max_body_size 32m;

    access_log  /var/www/example.com/access.log;
    error_log  /var/www/example.com/error.log;

    # SSL
    ssl on;
    ssl_certificate /var/www/example.com/cert/bundle.cer;
    ssl_certificate_key /var/www/example.com/cert/example.com.key;

    # Enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # Intermediate cypersuite as recommended by Mozilla
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    ssl_prefer_server_ciphers on;
    # Add HSTS (HTTPStrictTransportSecurity)
    add_header Strict-Transport-Security "max-age=31536000";

    # Directives to send expires headers and turn off 404 error logging.
    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        #expires max;
        log_not_found off;
        access_log off;
    }

    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    ## Disable viewing .htaccess & .htpassword
    location ~ /\.ht {
        deny  all;
    }

    location ^~ /admin/ {
                auth_basic "Restricted";
                auth_basic_user_file /var/www/example.com/.htpasswd;
                try_files $uri $uri/ /index.php$is_args$args;
                location ~ \.php$ {
                        include /etc/nginx/php-inside.conf;
                }
        }

    include /etc/nginx/php.conf;
}

Upvotes: 5

TomWardrop
TomWardrop

Reputation: 567

This is a more graceful solution that I use. Requires one server block for the actual website, and one server block for the redirect from non-www/non-https to https://www.*.

server {
  listen IP_ADDRESS:443 ssl;
  server_name www.domain.com;
}
server {
  listen IP_ADDRESS:80 ssl default_server;
  listen IP_ADDRESS:443 ssl default_server;
  return 301 https://www.domain.com$request_uri;
}

The default_server option is important, otherwise the first definition becomes the default which can work against your intentions of redirecting all requests other than www.domain.com. By using default_server, your redirect server block acts as a catch-all.

In my opinion though, you should NOT be using "www". You should be redirecting from www to non-www. www is a legacy thing that isn't relevant these days. You're perpetuating this irrelevant legacy by redirecting from non-www to www.

Upvotes: 0

Steffen Ullrich
Steffen Ullrich

Reputation: 123250

This is probably because you don't have a certificate for domain.com, but only for www.domain.com or *.domain.com. See Nginx redirect http://www and naked http/https to https://www or https://serverfault.com/questions/579916/nginx-redirect-https-www-to-https-non-www-without-untrusted-connection-warn/579917#579917 for details.

Upvotes: 4

Related Questions