Redirect HTTP to HTTPS in Nginx

In this guide, we will explain how to redirect the HTTP traffic to HTTPS in Nginx.

Nginx pronounced “engine x” is a free, open-source, high-performance HTTP and reverse proxy server responsible for handling the load of some of the largest sites on the Internet.

If you are a developer or system administrator, chances are that you’re dealing with Nginx on a regular basis. One of the most common tasks you’ll likely perform is redirecting the HTTP traffic to the secured (HTTPS) version of your website.

Unlike HTTP, where requests and responses are sent and returned in plaintext, HTTPS uses TLS/SSL to encrypt the communication between the client and the server.

There are many benefits of using HTTPS over HTTP, such as:

  • All the data is encrypted in both directions. As a result, sensitive information cannot be read if intercepted.
  • Google Chrome and all other popular browsers will mark your website as safe.
  • HTTPS allows you to use the HTTP/2 protocol, which significantly improves the site performance.
  • Google favors HTTPS websites. Your site will rank better if served via HTTPS.

The preferred method to redirect HTTP to HTTPS in Nginx is to configure a separate server block for each version of the site. You should avoid redirecting the traffic using the if directive , as it may cause unpredictable behavior of the server.

Redirect HTTP to HTTPS per Site

Typically when an SSL certificate is installed on a domain, you will have two server blocks for that domain. The first one for the HTTP version of the site on port 80, and the other for the HTTPS version on port 443.

To redirect a single website to HTTPS open the domain configuration file and make the following changes:

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

Let’s break down the code line by line:

  • listen 80 – The server block will listen for incoming connections on port 80 for the specified domain.
  • server_name linuxize.com www.linuxize.com – Specifies the server block’s domain names. Make sure you replace it with your domain name.
  • return 301 https://linuxize.com$request_uri – Redirect the traffic to the HTTPS version of the site. The $request_uri variable is the full original request URI, including the arguments.

Usually, you will also want to redirect the HTTPS www version of the site to the non-www or vice versa. The recommended way to do the redirect is to create a separate server block for both www and non-www versions.

For example, to redirect the HTTPS www requests to non-www, you would use the following configuration:

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

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

    # . . . other code

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

server {
    listen 443 ssl http2;
    server_name linuxize.com;

    # . . . other code
}

Whenever you make changes to the configuration files you need to restart or reload the Nginx service for changes to take effect:

sudo systemctl reload nginx 

Redirect All Sites to HTTPS

If all of the websites hosted on the server are configured to use HTTPS, and you don’t want to create a separate HTTP server block for each site, you can create a single catch-all HTTP server block. This block will redirect all HTTP requests to the appropriate HTTPS blocks.

To create a single catch-all HTTP block which will redirect the visitors to the HTTPS version of the site, open the Nginx configuration file and make the following changes:

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

Let’s analyze the code line by line:

  • listen 80 default_server – Sets this server block as the default (catch-all) block for all unmatched domains.
  • server_name __ is an invalid domain name that never matches any real domain name.
  • return 301 https://$host$request_uri – Redirect the traffic to the corresponding HTTPS server block with status code 301 (Moved Permanently). The $host variable holds the domain name of the request.

For example, if the visitor opens http://example.com/page2 in the browser, Nginx will redirect the request to https://example.com/page2.

Source: https://linuxize.com/post/redirect-http-to-https-in-nginx/

 


 

Redirect HTTP to HTTPS in Nginx

We see how to use Nginx to redirect any non-SSL connection to HTTP.

 

^ Ad space to help offset hosting costs 😀

A common task is redirecting any HTTP request to HTTPS, so our applications and sites are always using SSL certificates.

In Nginx, we generally want to avoid if statements. Similar to how we redirect between www and non-www subdomains, we’ll use a server block to redirect HTTP to HTTPS requests.

Redirect All HTTP

One method I like to do is catch-all port 80 (http) requests and redirect them to port 443 (https).

This works when you know you want all configured sites to definitely only use https.

server {
    listen 80 default_server;

    server_name _;

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

This listens on port 80. It’s also the port 80 default server. It’s assumed that it’s the only server block listening on port 80.

The server_name is _, which matches any hostname used.

Finally, we return a 301 redirect to the https version of whatever URI was requested. We know all requests reaching this server block is http, because it only listens for port 80 requests.

Redirect Specific Sites

We can redirect only specific sites also. This is better if you have multiple apps/sites and not all of them should be forced to use SSL certificates.

server {
    listen 80;

    server_name foo.com;
    return 301 https://foo.com$request_uri;
}

Here we listen on port 80 for http requests, but we don’t make it the default server. Instead, it is only listening for requests made to the hostname foo.com (where the Host HTTP header is set to foo.com).

It returns a 301 redirect to the https version of the URI given, but hard-codes the hostname (foo.com).

App Configuration

Our sites/apps can then be set to listen on port 443 for SSL connections only.

server {
    listen 443 ssl default_server;
    server_name foo.com;
}

server {
    listen 443 ssl;
    server_name bar.com;
}

# and so on...

Any site can be the default_server for port 443. That’s up to you to set and likely won’t matter in practice.

Source: https://serversforhackers.com/c/redirect-http-to-https-nginx


 

Configure Nginx SSL + force HTTP to redirect to HTTPS + force www to non-www on Serverpilot free plan (Using Nginx configuration file only)

Posted September 29, 2016 329k views

NginxUbuntuWordPressDigitalOceanConfiguration Management

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).

Source: https://www.digitalocean.com/community/questions/configure-nginx-ssl-force-http-to-redirect-to-https-force-www-to-non-www-on-serverpilot-free-plan-using-nginx-configuration-file-only