Nginx 301 and 302 redirects on a Linux VPS

3 min
VPS/VDS Hosting

Redirects keep your visitors and your search ranking when a page moves, when you switch domain or when you force HTTPS. This guide shows how to write clean Nginx redirects on your VPS Linux Infrawire, and how to test them before they reach production.

📋 Prerequisites

  • A Linux VPS (Ubuntu 24.04 / Debian 12 or AlmaLinux 9 / Rocky 9) with root SSH access. You find the IP and the root password in your customer area.
  • Nginx installed and serving at least one site (sudo apt install nginx or sudo dnf install nginx).
  • A domain name (example.com) whose DNS A/AAAA records point to VPS_IP.

🧭 301 or 302: which one to pick

  • 301 (Moved Permanently): the move is final. Browsers and search engines cache it and transfer the ranking to the new URL.
  • 302 (Found): the move is temporary (maintenance, A/B test). The old URL stays the reference.

If in doubt, use 302 first: a wrong 301 can stay cached in browsers for a long time.

⚙️ return or rewrite

Prefer return: it is faster and easier to read, because Nginx stops processing immediately.

Nginx
location = /old-page { return 301 /new-page; }

Use rewrite only when you must capture part of the URL with a regular expression:

Nginx
rewrite ^/blog/(\d+)/(.*)$ /articles/$2 permanent;

The flag permanent sends a 301, redirect sends a 302.

🌐 Redirect www to non-www

Create a dedicated server block that only redirects. Edit /etc/nginx/sites-available/example.com (Debian/Ubuntu) or /etc/nginx/conf.d/example.com.conf (AlmaLinux/Rocky):

Nginx
1server { 2 listen 80; 3 listen [::]:80; 4 server_name www.example.com; 5 return 301 $scheme://example.com$request_uri; 6}

For the opposite direction, swap the two host names. $request_uri keeps the path and the query string.

🔒 Redirect HTTP to HTTPS

Once a certificate is installed, send all clear-text traffic to HTTPS:

Nginx
1server { 2 listen 80; 3 listen [::]:80; 4 server_name example.com www.example.com; 5 return 301 https://example.com$request_uri; 6} 7 8server { 9 listen 443 ssl; 10 listen [::]:443 ssl; 11 http2 on; 12 server_name www.example.com; 13 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; 14 ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; 15 return 301 https://example.com$request_uri; 16}

The real site then lives in a third block listening on 443 with server_name example.com;. Note that http2 on; requires Nginx 1.25.1 or newer, which is the case on Ubuntu 24.04.

🔗 Old URL to new URL

For a few pages, use exact matches (=), which are the fastest:

Nginx
location = /about-us.html { return 301 /about; } location = /promo { return 302 /offers/current; }

For a whole folder that moved while keeping the sub-paths:

Nginx
location /docs/ { rewrite ^/docs/(.*)$ /help/$1 permanent; }

🏠 Redirect a whole domain

When you abandon old-example.com for example.com, keep every path:

Nginx
1server { 2 listen 80; 3 listen [::]:80; 4 server_name old-example.com www.old-example.com; 5 return 301 https://example.com$request_uri; 6}

If the old domain also has HTTPS, add a matching listen 443 ssl; block with its own certificate, otherwise browsers show a certificate error before the redirect.

✅ Test and reload cleanly

Always validate the syntax before reloading. A reload keeps current connections alive, a restart does not.

Bash
sudo nginx -t sudo systemctl reload nginx

Then check the headers without following the redirect:

Bash
curl -I http://www.example.com/old-page

Expected output (shortened):

HTTP/1.1 301 Moved Permanently
Server: nginx
Location: https://example.com/old-page

To follow the whole chain and see every hop, use curl -IL http://www.example.com/old-page. Ideally there is a single hop: avoid chains such as HTTP, then www, then HTTPS.

🛠️ Troubleshooting

Too many redirects (loop). A rule matches its own destination, often because a proxy or CDN already terminates HTTPS. Check the Location header with curl -I and make sure the target block does not redirect again.

The change has no effect. You edited a file that is not enabled. On Debian/Ubuntu, check the link in /etc/nginx/sites-enabled/, then run sudo nginx -t and sudo systemctl reload nginx. Browsers also cache 301: test with curl or a private window.

Query string lost. With rewrite ... ? or a fixed target, parameters vanish. Use $request_uri in return, or keep $args explicitly.

Wrong block answers. Nginx picks the block by server_name. List them with sudo nginx -T | grep server_name and look for duplicates.


🎉 Your redirects are live: permanent where it is final, temporary where it is not, and tested with curl before your visitors notice anything.

Stay up to date with the latest offers, discounts and special promotions.

View promotions

We use cookies

We use analytics cookies to improve your experience on our site. No data is sold to third parties. Learn more in our privacy policy.