Websites & apps

Redirect www to non-www (or the reverse) in Nginx

Pick one hostname for your site and send every other version to it with a clean 301 redirect in Nginx, keeping HTTPS and the full request path.

BeginnerLinux server5 min read

A site that answers on both www.example.com and example.com looks like two copies of the same content to search engines, and cookies or logins can behave oddly between them. The fix is to choose one hostname and permanently redirect the other to it. This guide shows the clean Nginx way: a small, separate server block that does nothing but return 301.

Before you start

  • A Linux VPS with Nginx serving your site. See Host multiple websites with Nginx server blocks if you have not set one up.
  • DNS records for both names pointing at the server. A redirect can only happen if the request reaches Nginx first.
  • A certificate that covers both names if you use HTTPS. With Certbot that means requesting -d example.com -d www.example.com.
  • SSH access with sudo.

Choose your main hostname

Either choice is fine for search ranking. What matters is picking one and sticking to it.

  • non-www (example.com): shorter and common for new sites.
  • www (www.example.com): useful if you want to point the root and www at different services, or rely on CNAME-based CDNs.

Make sure the site's own settings agree. In WordPress, for example, Settings → General must use the same hostname, or WordPress and Nginx will redirect back and forth.

Why a separate server block

You could write an if ($host =...) test inside your main block, but Nginx evaluates if on every request and it is easy to get wrong. A dedicated server block for the name you are redirecting away from is faster, clearer and cannot loop with your main site.

return 301 is also better than rewrite for this job. It stops processing immediately and sends the redirect.

Redirect www to non-www over HTTPS

This is the common setup once a site has SSL. Edit the site's file, for example /etc/nginx/sites-available/example.com on Ubuntu or Debian, or /etc/nginx/conf.d/example.com.conf on AlmaLinux and Rocky Linux. The file below has three blocks: the first sends all plain HTTP to the final address, the second redirects HTTPS on www, and the third is the real site.

Nginx
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_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

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

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    root /var/www/example.com/public;
    index index.html index.php;
}

Key points:

  • $request_uri keeps the path and query string, so https://www.example.com/blog?page=2 lands on https://example.com/blog?page=2.
  • The www HTTPS block still needs a certificate. The browser checks the certificate before it receives the redirect, so without one visitors see a security warning.
  • The HTTP block sends visitors to the final address in one hop instead of two.

Note: If Certbot manages your config, it may have added its own include lines and redirect blocks. Keep its ssl_certificate lines, and replace any duplicate redirect logic with the blocks above so there is only one rule per hostname.

Redirect non-www to www

The reverse is the same pattern with the names swapped:

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

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

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

Your main site block then uses server_name www.example.com;.

Sites without HTTPS

If the site has no certificate yet, a single extra block on port 80 is enough:

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

Change it to https:// once you add SSL. See Redirect HTTP to HTTPS in Nginx for the full HTTPS setup.

Apply the change

Test, then reload:

Bash
sudo nginx -t && sudo systemctl reload nginx

Tip: Test with a temporary 302 first if you are unsure. Browsers cache 301 redirects hard, so a mistake can stick in your own browser even after you fix it. Switch to 301 once it behaves.

Check it works

Use curl -I to see only the response headers. Test each combination:

Bash
curl -I http://www.example.com/test?x=1
curl -I https://www.example.com/test?x=1
curl -I http://example.com/test?x=1

Each should return:

HTTP/1.1 301 Moved Permanently
Location: https://example.com/test?x=1

Then confirm the final address serves the page:

Bash
curl -I https://example.com/

You want HTTP/1.1 200 OK (or HTTP/2 200). To follow the whole chain and count the hops, add -L:

Bash
curl -sIL http://www.example.com/ | grep -iE "^HTTP|^location"

Troubleshooting

  • Certificate warning on www: the certificate does not include www.example.com. Reissue it with both names: sudo certbot --nginx -d example.com -d www.example.com.
  • ERR_TOO_MANY_REDIRECTS: your app redirects to the other hostname while Nginx redirects back. Match the app's site URL to the Nginx choice. More causes in Fix ERR_TOO_MANY_REDIRECTS on Nginx.
  • Redirect drops the path: you used $uri or nothing at all. Use $request_uri, which keeps the path and query string.
  • Old redirect still happens after a fix: your browser cached the 301. Test with curl or a private window.
  • www does not respond at all: DNS has no record for www. Add an A record or a CNAME to the root domain.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.