32

I'm having an error when trying to redirect https://example.com to https://www.example.com.

When I go to https://example.com, it doesn't redirect and returns the page/200 status.

I don't want this, I want it to redirect to https://www.example.com.

When I go to http://example.com, it redirects to https://www.example.com

Can somebody tell me where I am going wrong?

This is my default and default-ssl configuration files:

default.conf

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

default-ssl.conf

upstream app_server_ssl {
    server unix:/tmp/unicorn.sock fail_timeout=0;
}

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

    listen 443;
    root /home/app/myproject/current/public;
    index index.html index.htm;

    error_log /srv/www/example.com/logs/error.log info;
    access_log /srv/www/example.com/logs/access.log combined;

    ssl on;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_certificate /srv/www/example.com/keys/ssl.crt;
    ssl_certificate_key /srv/www/example.com/keys/www.example.com.key;
    ssl_ciphers AES128-SHA:RC4-MD5:ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:RSA+3DES:!ADH:!AECDH:!MD5:AES128-SHA;
    ssl_prefer_server_ciphers on;

    client_max_body_size 20M;


    try_files $uri/index.html $uri.html $uri @app;


    # CVE-2013-2028 http://mailman.nginx.org/pipermail/nginx-announce/2013/000112.html
    if ($http_transfer_encoding ~* chunked) {
            return 444;
        }

    location @app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app_server_ssl;
    }

    error_page 500 502 503 504 /500.html;

    location = /500.html {
        root /home/app/example/current/public;
    }
}
2
  • 1
    What is the point of creating 2 conf files ? May 12, 2017 at 2:14
  • Separation of concerns, the non SSL config was so small that it seemed best to separate it from the SSL only config.
    – Thomas V.
    May 15, 2017 at 17:34

7 Answers 7

41

You are missing listen directive in file default-ssl.conf. Add listen 443; in this directive

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

By default, if you omit this directive, nginx assume that you want listen on port 80. Here the documentation of this default behavior.


Edit: Thanks for comment from @TeroKilkanen.

Here the complete config for your default-ssl.conf

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /srv/www/example.com/keys/ssl.crt;
    ssl_certificate_key /srv/www/example.com/keys/www.example.com.key;
    return 301 https://www.example.com$request_uri;
}

Sidenote: You can replace ssl on; directive with listen 443 ssl; as recommendation from nginx documentation.

5
  • 4
    You also need to set up ssl_certificate and ssl_certificate_key directives in this block, and use listen 443 ssl; so that it is an SSL vhost. Aug 29, 2014 at 7:04
  • Please post contents of current default-ssl.conf. Maybe some typo or reorder issue caused that.
    – masegaloeh
    Aug 29, 2014 at 14:09
  • This is embarrassing :\ The culprit was a duplicate nginx configuration in /etc/nginx/sites-enabled, /etc/nginx/sites-enabled/default-ssl.backup was interfering with any redirects in default-ssl. Silly error.
    – Thomas V.
    Aug 29, 2014 at 15:28
  • so I had to issue 2 certs: for www-domain and for non-www one
    – vladkras
    Jul 20, 2017 at 7:12
  • How can this be achieved on say port 5007: example.com:5007 to example.com:5007
    – CP3O
    Sep 11, 2018 at 12:38
8

Just throw in an if statement and you should be on your way. I checked the results in curl.exe -I and all cases besides https://www.example.com get treated as 301. SSL is tricky because it gets checked before you get 301 URL redirection. Hence, you get certificate errors.

Personally, I like removing the www's from the domain but I wrote my code below to answer your question.

server {
listen 443 ssl;
listen [::]:443 ssl; # IPV6

server_name example.com www.example.com; # List all variations here

# If the domain is https://example.com, lets fix it!

if ($host = 'example.com') {
  return 301 https://www.example.com$request_uri;
}

# If the domain is https://www.example.com, it's OK! No changes necessary!

... # SSL .pem stuff
...
}

server {
  listen 80;
  listen [::]:80;

  # If the domain is http://example.com or http://www.example.com, then let's change it to https!

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

The way I do it is to use an if statement inside the ssl server block that redirects to https of www

ssl_certificate /srv/www/example.com/keys/ssl.crt;
ssl_certificate_key /srv/www/example.com/keys/www.example.com.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AES128-SHA:RC4-MD5:ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:RSA+3DES:!ADH:!AECDH:!MD5:AES128-SHA;
ssl_prefer_server_ciphers on;
client_max_body_size 20M;

upstream app_server_ssl {
    server unix:/tmp/unicorn.sock fail_timeout=0;
}

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

server {
    listen 443 default_server ssl;
    server_name www.example.com;

    # redirect https://example.com to https://www.example.com
    # mainly for SEO purposes etc
    #we will use a variable to do that
    set $redirect_var 0;

    if ($host = 'example.com') {
      set $redirect_var 1;
    }
    if ($host = 'www.example.com') {
      set $redirect_var 1;
    }

    if ($redirect_var = 1) {
      return 301 https://www.example.com$request_uri;
    } 

    try_files $uri/index.html $uri.html $uri @app;

    # CVE-2013-2028 http://mailman.nginx.org/pipermail/nginx-announce/2013/000112.html
    if ($http_transfer_encoding ~* chunked) {
            return 444;
        }

    location @app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app_server_ssl;
    }

    error_page 500 502 503 504 /500.html;

    location = /500.html {
        root /home/app/example/current/public;
    }
}

Of course, whenever you want to use an if statement in an nginx config file; you should have read: https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/

1
  • the condition if ($host = 'www.example.com') is not necessary.
    – Karl.S
    Jun 23, 2017 at 22:07
2

Its 2018 now and i figured to give this one a renewed shot in case some one is looking for a simple solution.

My take on this as a relatively new-comer is to make things as simple as possible. Basically you wish to redirect both http://example.com and https://example.com to https://www.example.com. And that you only succeed in redirecting http://example.com

This is quite a straightforward operation requiring only two server blocks (i will demonstrate this briefly in a single config file)

# 1. Server block to redirect all non-www and/or non-https to https://www
server {
    # listen to the standard http port 80
    listen 80; 

    # Now, since you want to route https://example.com to http://www.example.com....
    # you need to get this block to listen on https port 443 as well
    # alternative to defining 'ssl on' is to put it with listen 443
    listen 443 ssl; 

    # define server_name
    server_name example.com *.example.com; 

    # DO NOT (!) forget your ssl certificate and key
    ssl_certificate PATH_TO_YOUR_CRT_FILE;
    ssl_certificate_key PATH_TO_YOUR_KEY_FILE; 

    # permanent redirect
    return 301 https://www.example.com$request_uri;  
    # hard coded example.com for legibility 
}
# end of server block 1. nearly there....

# 2. Server block for the www (primary) domain
# note that this is the block that will ultimately deliver content
server {
    # define your server name
    server_name www.example.com; 

    # this block only cares about https port 443
    listen 443 ssl;

    # DO NOT (!) forget your ssl certificate and key
    ssl_certificate PATH_TO_YOUR_CRT_FILE;
    ssl_certificate_key PATH_TO_YOUR_KEY_FILE; 

    # define your logging .. access , error , and the usual 

    # and of course define your config that actually points to your service
    # i.e. location / { include proxy_params; proxy_pass PATH_TO_SOME_SOCKET; }
}
# End of block 2.
# voilà! 

Now both http://example.com and https://example.com should redirect to https://www.example.com. Basically this setup redirects everything non-www and/or non-https to https://www.

1

In my case, to redirect from http://example.com to https://www.example.com. I configure my nginx with "IF condition" like this and it works for me.

  server {
        listen 80;

        server_name example.com;


            if ($host = example.com) {
                    return 301 https://www.$host$request_uri;
            }  


        location / {
            root   html;
            index  index.php index.html index.htm;
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

....

 server {
        server_name www.example.com;
        listen 80;
        return 404;  


 }
0

All the domain request redirecting to https://www.example.com/

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

server {
    listen 443 ssl http2;
    if ($host = example.com) {
        return 301 https://www.$host$request_uri;
    } # managed by Certbot
    server_name example.com www.example.com;
    ssl_certificate /etc/nginx/ssl/example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;

    location / {
        proxy_pass http://0.0.0.0:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
-1

To redirect all requests to https://www.example

create a server block for the redirect and primary domain on your SSL port (typically 443) as well as default http port 80

# non-www to ssl www redirect
server {
  listen 80; 
  listen 443 ssl;
  server_name example.com;
  return 301 https://www.example.com$request_uri;
  # ... ssl certs
}

# ssl setup for www (primary) domain
server {
  listen 80;
  listen 443 ssl;
  server_name www.example.com;
  if ($scheme = http) {
    return 301 https://www.example.com$request_uri;
  }
  # ... the rest of your config + ssl certs
}

save and follow with sudo nginx -s reload

This will redirect

http://example      301 -> https://www.example
https://example     301 -> https://www.example
http://www.example  301 -> https://www.example
https://www.example 200
3
  • You are missing a ;in the second server block in the if clause. It should be return 301 https://www.example.com$request_uri; Dec 5, 2016 at 9:24
  • 1
    However, would that even work? Can it listen for http on 443? Dec 5, 2016 at 15:36
  • you are right, i am missing listen 80, ive added it in. Dec 5, 2016 at 15:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .