Best way to configure Nginx SSL + force HTTP to redirect to HTTPS + force www to non-www on Serverpilot free plan (by using Nginx configuration file only)

September 29, 2016 35.3k views
Nginx DigitalOcean Configuration Management WordPress Ubuntu

I have set up a Wordpress website running on my Serverpilot/Digital Ocean droplet. I have succesfully managed to:

  • Manually Install a PositiveSSL certificate (from Namecheap)
  • Change the APPNAME.conf file to force HTTP to redirect to HTTPS and to force www to non-www

So everything works fine but I'm not sure if I have configured the Serverpilot Nginx configuration file correctly. My questions are:

What I did: I have implemented the 301 redirects rules in Serverpilots Nginx configuration file (APPNAME.conf) so the redirects are handled in the most efficient way possible by the server.**
My question: is this indeed the best/fastest way?

What I did: The config files should be protected from serverpilot updates, so when serverpilot updates it's system, no files get overwritten, no errors will take place. I followed this Serverpilot Article
My question: Is my configuration protected from being overwritten by serverpilot?

What I did: Reduce the amount of redirects. All requests (http//example.com, http://www.example.com, https://www.example.com) should be redirected to https://example.com in max. one redirect step. I have tested my website in HTTP Status Code Checker. All redirects are done in max. one step.

My question: When I test for instance Google.nl in this tool I see that the redirect from http://google.nl to https://www.google.nl is performed in two steps. Why is that? Should I also perform a redirect from http://www.example.com to https://example.com in two 301 redirects?

My Serverpilot Nginx configuration

Step 1: Installing SSL certificat

I have installed the PositiveSSL certificate on the server following step 1 - 4 of this tutorial: https://www.blogmehow.com/how-to-manually-install-ssl-on-serverpilot-free-plan-1331/

Step 2: Create 3 serverblocks in the APPNAME.custom.conf file

I have renamed the APPNAME.conf file to APPNAME.custom.conf file in this /etc/nginx-sp/vhosts.d opened it and changed it so it has three serverblocks. One to redirect http to https, one to redirect the https www-name to no-www, and one to actually handle the requests.:

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

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name www.example.com; 

ssl on;

ssl_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;
ssl_certificate_key /root/certs/APPNAME/ssl.key;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

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:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; 

ssl_stapling on; 
ssl_stapling_verify on;

ssl_trusted_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;

root   /srv/users/serverpilot/apps/APPNAME/public;

access_log  /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.access.log  main;
error_log  /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.error.log;

proxy_set_header    Host              $host;
proxy_set_header    X-Real-IP         $remote_addr;
proxy_set_header    X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header    X-Forwarded-SSL on;
proxy_set_header    X-Forwarded-Proto $scheme;

include /etc/nginx-sp/vhosts.d/APPNAMEd/*.nonssl_conf;
include /etc/nginx-sp/vhosts.d/APPNAME.d/*.conf;

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

server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;

ssl on;

ssl_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;
ssl_certificate_key /root/certs/APPNAME/ssl.key;

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:20m;
ssl_session_tickets off;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

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:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; 

ssl_stapling on; 
ssl_stapling_verify on;

ssl_trusted_certificate /root/certs/APPNAME/APPNAME_nl.chained.crt;

root   /srv/users/serverpilot/apps/APPNAME/public;

access_log  /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.access.log  main;
error_log  /srv/users/serverpilot/log/APPNAME/APPNAME_nginx.error.log;

proxy_set_header    Host              $host;
proxy_set_header    X-Real-IP         $remote_addr;
proxy_set_header    X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header    X-Forwarded-SSL on;
proxy_set_header    X-Forwarded-Proto $scheme;

include /etc/nginx-sp/vhosts.d/APPNAME.d/*.nonssl_conf;
include /etc/nginx-sp/vhosts.d/APPNAME.d/*.conf;
}

I hope someone can take the time to have a look at my configuration and let me know how I can optimize my setup even more taking into account my goals.

Kind regards,

Jan

** According to what I have found in This article, this is the most efficient and fastest way to do a 301 redirect: The best way to accomplish this is using three server blocks: one to redirect http to https, one to redirect the https www-name to no-www, and one to actually handle requests. The reason for using extra server blocks instead of ifs is that server selection is performed using a hash table, and is very fast. Using a server-level if means the if is run for every request, which is wasteful. Also, capturing the requested uri in the rewrite is wasteful, as nginx already has this information in the $uri and $request_uri variables (without and with query string, respectively).

4 Answers

Thanks for sharing this. I am not aware of a good technical reason for doing the redirect in two steps but would guess that Google uses some more complex logic to determine where to redirect a visitor in order to catch edge cases and make Google usable for everyone. Google also likely started redirecting to www before they implemented the redirect to https.

anyone who uses serverpilot, please join to the unofficial slack discussion community hub, for sharing experiences about doing missing (premium) features in free plan 😉

http://serverpilot-slack.herokuapp.com/

Thanks a lot for sharing this setup! I was struggling with a nginx config to redirect http:// to https:// and then www.domain.com to domain.com. But after I saw your setup I decided to try it out with a few tweaks and now my vhost looks roughly like this:

server {
    listen 80;
    server_name www.domain.com domain.com;

    # redirects both www and non-www to https
    return 301 https://domain.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name www.domain.com;

    # redirects www to non-www. wasn't work for me without this server block
    return 301 $scheme://domain.com$request_uri;
}

server {

    ## SSL settings

    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;

    server_name domain.com;

    include snippets/ssl-domain.com.conf;
    include snippets/ssl-params.conf;

    # other vhost configuration
}

Maybe it could help you shorten your code as well. You should try to make a separate file for your SSL configuration and just include it with the include as I did in my 3rd server block. It's cleaner and easier to maintain.

Using a https error code 497, it is possible to do it in two server blocks.

This is tested and working on my site.

server {
    listen 80;
    server_name www.domain.com domain.com;

    # redirects both www and non-www to ssl port with http (NOT HTTPS, forcing error 497)
    return 301 http://domain.com$request_uri;
}

server {
    listen 433 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;

    server_name domain.com;
    error_page 497 https://domain.com$request_uri;

    include snippets/ssl-domain.com.conf;
    include snippets/ssl-params.conf;

    # other vhost configuration
}

Hope this helps!

Have another answer? Share your knowledge.