Question

How to Redirect All Requests to HTTPS-WWW in a single redirect [Nginx]

Posted September 20, 2020 164 views
Nginx

I am currently running a LEMP stack for my WordPress project. The domain I am using serves over HTTPS+WWW (https://www.example.com), which means all of the following requests are redirected to it:

Right now, all of these requests are being redirected to HTTPS+WWW as part of the redirect rules created by certbot during the setup. Below is my current server configuration.

server {
        root /var/www/html;
        index index.php index.html index.htm index.nginx-debian.html;
        server_name example.com www.example.com;
        client_max_body_size 64m;

        location / {
                #try_files $uri $uri/ =404;
                try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
        }

        location ~ /\.ht {
                deny all;
        }

        location = /favicon.ico { log_not_found off; access_log off; }
        location = /robots.txt { log_not_found off; access_log off; allow all; }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
           expires max;
           log_not_found off;
        }

        location ~* ^/xmlrpc.php$ {
                return 403;
        }

        # WordPress: deny general stuff
        location ~* ^/(?:xmlrpc\.php|wp-links-opml\.php|wp-config\.php|wp-config-sample\.php|wp-comments-post\.php$
                deny all;
        }

    listen 443 ssl http2; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}

server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        server_name example.com www.example.com;
    return 404; # managed by Certbot

    expires $expires;
}

Generally, a user would input HTTP + NON-WWW requests in the browser (like just example.com). When I checked via Varvy Redirect Mapper, I could see that the HTTP + NON-WWW request has two redirects. Here is a screenshot.

https://i.imgur.com/BEE9RQo.png

I want to resolve all the requests in just a single redirect to HTTPS-WWW (https://www.example.com). I have tried a couple of logics and also looked over to several other examples online but couldn’t find a proper solution.

Could someone please help?

These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.

×
1 answer

Hi @dhananjaygbhardwaj,

In the second server block, you should replace https://$host$request_uri; with https://www.example.com$request_uri; like this:

server {
    if ($host = www.example.com) {
        return 301 https://www.example.com$request_uri;
    } # managed by Certbot


    if ($host = example.com) {
        return 301 https://www.example.com$request_uri;
    } # managed by Certbot

...
}
  • Thanks, @tomnguyen I tried this and it’s still the same. Does the second server block in my config needs to be moved to the top?

    • @dhananjaygbhardwaj, you don’t need to move the second server block to the top. However, after editing the second server block, you must test the new configuration:

      • sudo nginx -t

      If you didn’t make any mistakes, you would see the following:

      nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
      nginx: configuration file /etc/nginx/nginx.conf test is successful
      

      Next, reload the nginx service to apply the changes:

      • sudo systemctl reload nginx.service

      Finally, test your website.

    • @dhananjaygbhardwaj, please disable Cloudflare temporarily, then use the curl tool to test the redirects:

      • curl -I http://example.com

      Then let me know the output. We will continue to diagnose more.

      • Yes, I have bypassed it from their dashboard. Here’s the output for the aforementioned command:

        HTTP/1.1 301 Moved Permanently
        Date: Sun, 20 Sep 2020 15:58:00 GMT
        Connection: keep-alive
        Cache-Control: max-age=3600
        Expires: Sun, 20 Sep 2020 16:58:00 GMT
        Location: https://example.com/
        cf-request-id: 054dd59842000026f1519bf200000001
        X-Content-Type-Options: nosniff
        Server: cloudflare
        CF-RAY: 5d5cbed39a9926f1-IAD
        alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
        
      • Okay, while I was there in CF dashboard, I saw an option “Always use HTTPS” being enabled, which was enabled while this project of mine was on a shared hosting plan, with no server-level HTTPS redirects whatsoever.

        I quickly disabled that option and rechecked with curl, and it resolved in a single redirect. I might just have to go through CF settings again to ensure that no uneccessary rules/features are applied.

        Thank you @tomnguyen so much! I do have a further question.

        Supposedly I do not want Certbot to manage my redirects, and want to do it manually (sounds absurd, but I am curious). How should I do it? Below is a sample that I saw while digging into the matter earlier. Is it the right approach?

        server {
            listen 80;
            server_name example.com www.example.com;
            return 301 https://www.example.com$request_uri;
        }
        
        server {
            listen 443 ssl http2;
            server_name example.com;
            ssl_certificate /etc/letsencrypt/live/example.com /fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
            include /etc/letsencrypt/options-ssl-nginx.conf;
            ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        
            return 301 https://www.example.com$request_uri;
        } 
        
      • Great, I will proceed with that instead. One more thing that I want to clarify.

        In the sample I just mentioned in the previous reply, do I need the second server block listening to 443?

        In the first server block (in the original question), I already have both example.com and www.example.com listening on 443. Sorry for the mess.

        If possible, could you please modify the original server config file I provided and post it with the recommended redirect method? Removing everything that’s not needed?

        • In my opinion, you should replace the second server block in the original server config file with the two server blocks in your last reply. Then modify the first server block in the original server config file as follows:

          • Change server_name example.com www.example.com; to server_name www.example.com; because you have a server block for example.com later.
          • Remove the following location block:

            location ~* ^/xmlrpc.php$ {
               return 403;
            }
            

            Because the next location block (# WordPress: deny general stuff) already includes /xmlrpc.php.

          By the way, I don’t write the final configuration here because there is a long line that has been truncated in your first question. Perhaps you copied the nano editor’s content, and that line did not fit your terminal screen. That line is:

          location ~* ^/(?:xmlrpc\.php|wp-links-opml\.php|wp-config\.php|wp-config-sample\.php|wp-comments-post\.php$
          
Submit an Answer