✓ Solved

Redirect www to non-www while also redirecting http to https with nginx

Hello,

I have constructed the virtual host configuration file below for nginx. It's mostly the default plus the modification done by certbot. I'm wondering if I have done this correctly. I have a feeling this is done rather sloppily and that it could be improved. It seems to be working correctly though. My goals are:

  • Redirect www to non-www domain name
  • Redirect http to https

A review and suggestions for improving this configuration file would be appreciated.

server {
        if ($host = www.domain.example) {
                return 301 $scheme://domain.example$request_uri;
        }

        if ($host != domain.example) {
                return 404;
        }

        root /var/www/html;

        index index.html index.nginx-debian.html;

        server_name domain.example www.domain.example;

        location / {
                try_files $uri $uri/ =404;
        }

        listen 443 ssl default_server;
        listen [::]:443 ssl ipv6only=on default_server;
        ssl_certificate /etc/letsencrypt/live/domain.example/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/domain.example/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
        if ($host = www.domain.example) {
                return 301 https://$host$request_uri;
        }

        if ($host = domain.example) {
                return 301 https://$host$request_uri;
        }

        listen 80 default_server;
        listen [::]:80 default_server;

        server_name domain.example www.domain.example;
        return 404; # managed by Certbot
}

Edit:
If I request the URL http://www.domain.example I'm getting two 301 redirects in the console. This is what I suspected would happen and probably isn't the cleanest way of doing this. See: https://i.imgur.com/6FdsZJw.png

4 Replies

✓ Best Answer

Yes, each page has its own canonical URL. Since I’m handling this in my web framework (Ruby on Rails) instead of the server level (nginx), it’s easy enough for me to add {% canonical_tag %} in my base template which then renders out the correct url for each page as it’s served.

As for http2, my understanding is that it’s an improved protocol for pipelining multiple requests to the browser in parallel. It doesn’t need to be on the http port, since that just gets immediately forwarded to https which runs http2. I imagine there’s two kinds of users, those who are running modern browsers which connect to https directly, and those running older clients which try http first, and the older clients might get confused by http2.

I have the same setup on my website (http, https, www and non-www, with https://non-www as the canonical site). But much simpler:

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  server_name reiterate-app.com www.reiterate-app.com;
  include local-conf.d/bot-filters.conf;
  location / {
    return 301 https://$host$request_uri;
  }
}

server {
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;
  server_name reiterate-app.com www.reiterate-app.com;

  include local-conf.d/bot-filters.conf;

  ssl_certificate /etc/letsencrypt/live/reiterate-app.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/reiterate-app.com/privkey.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

  location / {
         try_files $uri @backend;
  }
}

Two things. First, this avoids any “if” statements in your config, which I believe are to be avoided because they are slow.

Second, I’m not actually doing any redirects to remove www. Instead, I use canonical link tags. In the HTML of every page there’s a link like

<link href="https://reiterate.app" rel="canonical" />

And that lets services like google know what the preferred URL should be. So even if a bot gets there through some www link, it knows to index only the non-www.

Hi procyon,

So the canonical URL would be different for each page right? So for example if you had the following page "about.html" and someone would end up there with the "www" in the host. The canonical URL tag in the HTML source would be:

<link href="https://reiterate.app/about.html" rel="canonical" />

Also, why is there http2 in the HTTPS section, but not the HTTP section?

Thank you, I'm using Hugo and I can do something similar with that.

This is the final configuration I landed on and it works well without double redirects.

server {
    return 301 https://domain.example$request_uri;

    listen 80 default_server;
    listen [::]:80 ipv6only=on default_server;

    server_name domain.example www.domain.example;
}

server {
    return 301 https://domain.example$request_uri;

    server_name www.domain.example;

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

    ssl_certificate /etc/letsencrypt/live/domain.example/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.example/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/domain.example/chain.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
    root /var/www/html;

    index index.html index.nginx-debian.html;

    server_name domain.example;

    location / {
            try_files $uri $uri/ =404;
    }

    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 ipv6only=on default_server;
    ssl_certificate /etc/letsencrypt/live/domain.example/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.example/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/domain.example/chain.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

Reply

Please enter an answer
Tips:

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct