How to: Force SSL sitewide with Nginx

MannDude

Just a dude
vpsBoard Founder
Moderator
For those of you who run the Nginx webserver on your VPS and wish to force all visitors to access your site via SSL you can do this quite easily.

Before the upgrade to IPB4, vpsBoard still ran site-wide https:// but the conversion was software based and wasn't forced by the web server (even though I previously thought I was forcing all content to be over https at the server level...) This was discovered when non-forum pages (Such as the txt file that StatusCake pings for uptime monitoring) was accessible to me via both http and https. Not a huge deal, but someone else also reported some strange SSL and access issues after the upgrade which forced me to review the matter further.

Anyhow, the fix was quite simple and I figured I'd share it for those interested in doing the same. This assumes you already have your SSL installed and working on your server. You may be running software such as Wordpress, Joomla, or any other random software installation that would normally force SSL for you but you can do this at the server level as well quite easily and more reliably.

All you need to do is add the following line to your website's nginx configuration:

rewrite ^/(.*) https://your-site.com/$1 permanent;
That's it. Nothing spectacular but worth sharing.
 
Last edited by a moderator:

tonyg

New Member
This is all fine but doing this blindly will result could result in unwanted 301 redirects for all accessed documents.

The proper way would be the initial redirect from above (if needed) with SPDY taking over afterwards.
 

MannDude

Just a dude
vpsBoard Founder
Moderator
This is all fine but doing this blindly will result could result in unwanted 301 redirects for all accessed documents.

The proper way would be the initial redirect from above (if needed) with SPDY taking over afterwards.
Care to elaborate further on this?
 

tonyg

New Member
Let me start by repharing the "all accessed" part of my response. It should read all accessed documents without https.

In a proper configuration, SPDY avoids hitting the 301 redirect after the initial hit.
 

telephone

New Member
Care to elaborate further on this?
Probably referencing that your rewrite/return should be in a different server block:

Code:
# Redirect HTTP traffic to the HTTPS host
server {
    listen 0.0.0.0:80;
    listen [::]:80;

    server_name YOUR_SERVER_FQDN;
    server_tokens off;

    return 301 https://$server_name$request_uri;

    # ...
}

# HTTPS host
server {
    listen 0.0.0.0:443 ssl;
    listen [::]:443 ssl;

    server_name YOUR_SERVER_FQDN;
    server_tokens off;

    # ...
}
 
Last edited by a moderator:

tonyg

New Member
Yes, thank you @telephone.

To enable SPDY on nginx with the above config:

Code:
# Redirect HTTP traffic to the HTTPS host
server {
    listen 0.0.0.0:80;
    listen [::]:80;

    server_name YOUR_SERVER_FQDN;
    server_tokens off;

    return 301 https://$server_name$request_uri;

    # ...
}

# HTTPS host
server {
    listen 0.0.0.0:443 ssl spdy;
    listen [::]:443 ssl spdy;

    server_name YOUR_SERVER_FQDN;
    server_tokens off;

    location ~ \.html$ {
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
Code:
    }
Code:
    location ~ \.php$ {
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
Code:
    }
Code:
    # ...
}
 
Last edited by a moderator:

bohdans

New Member
OK, so you are all 'correct' but you would need the redirect for each server_name or to list every server_name in section.
If you add a domain/subdomain you will have to update that list.

The following code segment could be placed on a server, and then EVERY domain/host/subdomain will be redirected to HTTPS.

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

    return 301 https://$host$request_uri;
}
 

bohdans

New Member
The following commands would set your nginx install to force all connections to HTTPS in debian,

Code:
cat << 'EOF' > /etc/nginx/sites-available/forcessl.conf
server {
    listen 0.0.0.0:80;
    listen [::]:80;

    return 301 https://$host$request_uri;
}
EOF
ln -s /etc/nginx/sites-available/forcessl.conf /etc/nginx/sites-enabled/

service nginx reload
 

bohdans

New Member
Thought I might update this with a small change,

server {
listen 0.0.0.0:80 default;
listen [::]:80 default;
return 301 https://$host$request_uri;
}This will set this listener as the default, so if another site has port 80 enabled as well it will be ignored (unless it is on a subdomain? not sure about that)
 

eva2000

Active Member
Don't forget to set up HSTS, and to add your site to the HSTS preload list. That's absolutely necessary to prevent SSL stripping attacks and the like.
be careful with preload all subdomains need to be HSTS/https too for the domain and undoing the HSTS from preload takes ages.. so if you have some subdomains off the domain using non-https might not be the best idea for preload ?
 

joepie91

New Member
Don't forget to set up HSTS, and to add your site to the HSTS preload list. That's absolutely necessary to prevent SSL stripping attacks and the like.
be careful with preload all subdomains need to be HSTS/https too for the domain and undoing the HSTS from preload takes ages.. so if you have some subdomains off the domain using non-https might not be the best idea for preload ?
The correct solution to that, is to make all subdomains accessible over HTTPS too :)

It taking a long time to get rid of HSTS is by design - that's what makes it secure, and what prevents SSL stripping attacks.
 
Top