Situation

I have three subdomains on my site now. I want to move to full-site SSL connection on two of them to increase site security. That is to say, to redirect all traffic on http://subdomain.domain.net to https://subdomain.domain.net.

Solution

My site runs on nginx and I created three virtual hosts, all in the form

server {
	listen 443 ssl;

	ssl on;
	ssl_certificate /certs/ssl.crt;
	ssl_certificate_key /certs/ssl.key;

	root /wwwroot;
	
	server_name example.net;

	location / {
    	proxy_set_header X-Forwarded-Proto $scheme;
    	proxy_set_header Host $host; # !IMPORTANT: Hard to debug
    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	proxy_pass http://localhost:1234/;
	}
	
}

IMPORTANT NOTICE: When passing to node.js, proxy_set_header Host $host; should NEVER be omitted. This could cause your application to misfire 301 redirects on the same location, thus creating hard to debug REDIRECT LOOPs!

There are many ways to redirect the user, but among them are many pitfalls, which you should avoid.

Wrong Solutions

Multiple server blocks

Some people on StackOverflow suggests that we use a separate server block on redirecting, which is true. But they often implied that only one domain needs redirecting, and their answer gives you an illusion that we should give each SSL block a corresponding non-SSL redirecting block, which is abosoluty wrong.

Actually only one server block is necessary.

$server_name Usage

A lot of solutions on the Internet uses $server_name in return 301 statement. Look at the following code:

server {
    ...
    server_name ex1.ex.net;
    server_name ex2.ex.net;
    
    return 301 https://$server_name$request_uri;
}

Do you see any problems with that? Actually by such a configuration, users will always be redirected to ex1.ex.net, because $server_name only correspond to the first server_name entry.

REMEMBER here you should use $host instead.

Final Configuration

The SSL part is just the same as above

server {
	listen   your-ip-address:80 default_server;
    
	server_name ex1.ex.net;
    server_name ex2.ex.net;
    
    return 301 https://$host$request_uri;
}

Notes

To ensure your cookies not to be sent in plain text during redirecting, ALWAYS remember to add secure flag to it.