Uncertain non-www redirect is right

My web server is Nginx 1.22.1, run on Debian 12 virtual server, accessed over ssh.

Below is the relevant part of my config for all server blocks.

It was created by Certbot, except I added www. to the line return 301 https://www.$host$request_uri; in order to redirect non-www requests to www. (Sorry, I cannot highlight this piece withing the code block.)

The question is, have I amended the config with the www. correctly? Somehow requests are not being redirected to www. I’ve tried several browsers on two devices.

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

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

   listen 80;
   server_name example.ru www.example.ru;
    return 404; # managed by Certbot
}
1 Like

I think the issue here is that the $host variable will have the ‘www’ already on it, so it duplicate the www in the url. You should be able to use the server block below to simplify it:

server {
    listen 80 default_server;

    server_name example.ru www.example.ru;

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

Below is complete config file for one website, before I amended it with “www”. Could you take a look please?

server {
   server_name example.ru www.example.ru;

    ##
    # Include general vhost configuration snippet
    ##
    include /etc/nginx/snippets/general-vhost.conf;
    include /etc/nginx/snippets/general-security.conf;

    ##
    # Logging
    ##
    access_log  /var/www/example.ru/logs/access.log;
    error_log   /var/www/example.ru/logs/error.log info;

   root /var/www/example.ru/html;
   index index.html;


    #
    #   Default configuration.
    #
    location / {

        #
        #   Since we deliver files with no extension, we need to tell
        #   Nginx what those files are.
        #
        default_type text/html;

        #
        #   First attempt to serve request as file, then
        #   as directory, then fall back to displaying a 404.
        #
        try_files $uri $uri.html $uri/ =404;

    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.ru/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.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


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


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

}

Also, general-vhost.conf snippet used in the file above:

# favicon.ico
location = /favicon.ico {
    log_not_found off;
    access_log off;
}

# robots.txt
location = /robots.txt {
    log_not_found off;
    access_log off;
}

And general-security.conf snippet:

# Basic Security Headers
# add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

# . files
location ~ /\.(?!well-known) {
    deny all;
}

Moving this thread to the Troubleshooting category.

It looks like the redirect isn’t working because $host in the second condition still holds example.ru, so adding www. to it results in www. example.ru.example.ru. Instead, try explicitly setting return 301 https: //www.example.ru$request_uri;. Also, consider handling redirects outside the if blocks within the server block for better reliability.

I appreciate the response but the if clauses were added by Certbot at installation.

Redirecting apparently did not occur because the clauses only affected port 80 and http requests.

I’ve been recommended on Let’s Encrypt forum (the group that makes Certbot) to add these lines within port 443 server:

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

Now both http://example.ru and https://example.ru are being redirected to https://www.example.ru