In Nginx, how can I rewrite all http requests to https while maintaining sub-domain?

Solution 1:

Correct way in new versions of nginx

Turn out my first answer to this question was correct at certain time, but it turned into another pitfall - to stay up to date please check Taxing rewrite pitfalls

I have been corrected by many SE users, so the credit goes to them, but more importantly, here is the correct code:

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

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000" always; 

       [....]
}

Solution 2:

NOTE: The best way to do this was provided by https://serverfault.com/a/401632/3641 - but is repeated here:

server {
    listen         80;
    return 301 https://$host$request_uri;
}

In the simplest case your host will be fixed to be your service you want to send them to - this will do a 301 redirect to the browser and the browser URL will update accordingly.

Below is the previous answer, which is inefficient due to regex, a simple 301 is great as shown by @kmindi

I have been using nginx 0.8.39 and above, and used the following:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

Sends a permanent redirect to the client.


Solution 3:

I think the best and only way should be using a HTTP 301 Moved Permanently redirect like this:

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

The HTTP 301 Moved Permanently redirect is also the most efficient because there is no regex to be evaluated, according to already mentioned pitfails.


The new HTTP 308 Moved Permanently preserves the Request method and is supported by major browsers. For example, using 308 prevents browsers from changing the request method from POST to GET for the redirect request.


If you want to preserve the hostname and subdomain this is the way.

This does still work if you have no DNS, as I am also using it locally. I am requesting for example with http://192.168.0.100/index.php and will get redirected to exactly https://192.168.0.100/index.php.

I use listen [::]:80 on my host because i have bindv6only set to false, so it also binds to ipv4 socket. change it to listen 80 if you don't want IPv6 or want to bind elsewhere.

The solution from Saif Bechan uses the server_name which in my case is localhost but that is not reachable over a network.

The solution from Michael Neale is good, but according to the pitfails, there is a better solution with redirect 301 ;)


Solution 4:

Within the server block you can also do the following:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}

Solution 5:

The above didn't work for with with new subdomains being created all the time. e.g. AAA.example.com BBB.example.com for about 30 subdomains.

Finally got a config working with the following:

server {
  listen 80;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}
server {
  listen  443;
  server_name example.com;
  ssl on;
  ssl_certificate /etc/ssl/certs/myssl.crt;
  ssl_certificate_key /etc/ssl/private/myssl.key;
  ssl_prefer_server_ciphers       on;
# ...
# rest of config here
# ...
}