Updated: 12 October 2026 · Applies to: nginx 1.28.3 on Ubuntu 26.04 (tested) and nginx 1.26.3 on AlmaLinux 10 (file locations checked); the same directives work on any current nginx
To redirect a URL in nginx, add a return line to the site's server block: location = /old-page.html { return 301 /new-page.html; } for one page, or return 301 https://$host$request_uri; in the port 80 block to send all HTTP traffic to HTTPS. Then check the configuration with sudo nginx -t and apply it with sudo systemctl reload nginx. Use 301 for permanent moves and 302 for temporary ones.
Where the redirect goes
- Ubuntu and Debian: the site file in
/etc/nginx/sites-available/, enabled through a link in/etc/nginx/sites-enabled/. - AlmaLinux, Rocky Linux and RHEL: a
.conffile in/etc/nginx/conf.d/.
Find the server block whose server_name matches your domain. More on the file layout in nginx configuration explained. On a server with a control panel such as cPanel, make redirects with the panel's redirect tool or in .htaccess instead (redirect a website in cPanel).
Step by step
- Back up the file:
sudo cp /etc/nginx/sites-available/example.com /root/example.com.bak - Add the redirect (examples below) and save.
- Test the syntax:
sudo nginx -t. It must end withtest is successful. - Reload without dropping connections:
sudo systemctl reload nginx - Check the answer with curl rather than a browser, because browsers can cache a 301:
$ curl -I "http://example.com/page.html?a=1&b=2" HTTP/1.1 301 Moved Permanently Server: nginx/1.28.3 (Ubuntu) Location: https://example.com/page.html?a=1&b=2
All output in this article comes from our nginx test server with example.com names (some header lines left out).
1. Redirect one URL to another
location = /old-page.html {
return 301 /new-page.html;
}
$ curl -I https://example.com/old-page.html HTTP/1.1 301 Moved Permanently Location: https://example.com/new-page.html
location = matches only that exact path. A path on the same site is turned into a full URL by nginx. To send visitors to another site, write the full address: return 301 https://docs.example.org/;
Query strings: return does not pass them on by itself. A request for /old-page.html?utm=1 went to /new-page.html without ?utm=1. To keep them, add $is_args$args:
return 301 /new-page.html$is_args$args;
That sent /keep-args?utm=1 to /new-page.html?utm=1, and without a query string it added nothing.
2. Redirect HTTP to HTTPS
Your HTTPS site needs a working certificate first. Then let the port 80 block do nothing but redirect:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
$request_uri is the path plus the query string, so the query string survives, as in the curl test above. Writing the main host name instead of $host also moves www visitors to the bare domain in the same step:
$ curl -IL http://www.example.com/blog/hello.html HTTP/1.1 301 Moved Permanently Location: https://example.com/blog/hello.html HTTP/1.1 200 OK
For a catch-all that sends every host name on the server to HTTPS, use listen 80 default_server; with server_name _; and return 301 https://$host$request_uri;. Keep only one default_server per port.
3. Redirect www to non-www over HTTPS
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
The certificate must also cover www.example.com, or browsers show a warning before the redirect happens. https://www.example.com/blog/hello.html?x=1 went to https://example.com/blog/hello.html?x=1. For the opposite direction, see redirect non-www to www over SSL.
4. Redirect an old domain to a new one
server {
listen 80;
listen 443 ssl;
server_name old.example.net www.old.example.net;
ssl_certificate /etc/ssl/old.example.net/fullchain.pem;
ssl_certificate_key /etc/ssl/old.example.net/privkey.pem;
return 301 https://example.com$request_uri;
}
Each old page lands on the same path of the new domain: http://old.example.net/contact?x=1 went to https://example.com/contact?x=1. The old domain still needs its DNS records and a certificate for HTTPS visitors.
5. Redirect a folder or a pattern with rewrite
location /old-blog/ {
rewrite ^/old-blog/(.*)$ /blog/$1 permanent;
}
/old-blog/hello.html?ref=x went to /blog/hello.html?ref=x. Unlike return, rewrite keeps the query string by itself; end the target with ? to drop it (rewrite ^ /new-page.html? permanent;). The flag permanent gives a 301 and redirect a 302. Use return when you do not need the regular expression: it is simpler and stops processing at once.
6. Many redirects with a map
For a list of old and new paths, a map (outside the server block, in the same file) keeps the configuration readable:
map $uri $redirect_uri {
default "";
/old-pricing /pricing.html;
/2019/promo /offers;
}
server {
...
if ($redirect_uri) {
return 301 $redirect_uri;
}
}
/old-pricing went to /pricing.html. $uri has no query string, so add $is_args$args to the return line if you need to keep it.
Which redirect code to use
- 301: permanent. According to Google, its indexing uses a permanent redirect as a signal that the new URL should be the canonical one; a temporary redirect is not used that way.
- 302: temporary, for example a sale page or maintenance.
return 302 /offers;answered302 Moved Temporarily. According to nginx,returnwith only a full URL also gives a 302. - 307 and 308: the temporary and permanent versions that keep the request method, so a form POST stays a POST.
return 308 /form-new;answered308 Permanent Redirect.
The differences are explained in 301, 302, 307 and 308 compared.
Common mistakes
- Redirect loop: a
return 301 https://$host$request_uri;placed in the HTTPS block sends visitors back to the same URL. curl stopped withcurl: (47) Maximum (50) redirects followed; browsers report too many redirects. Behind Cloudflare with the Flexible SSL mode, an HTTP to HTTPS redirect on the server loops too; according to Cloudflare, switch to Full or Full (strict) or remove the server-side redirect. - Missing semicolon:
nginx -tanswered[emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:44andtest failed. nginx keeps running the old configuration until the test passes and you reload. - Wrong code:
return 200 /offers;does not redirect; it sent the text/offersas the page. Redirect URLs only work with 301, 302, 303, 307 and 308. - Old rule in the way: an exact
location =match always wins, but regular expression locations are checked in the order they appear, so an earlier pattern can catch the URL first.
Using Apache instead? See 301 redirects in Apache and redirect HTTP to HTTPS with .htaccess.
Official documentation: nginx rewrite module (return and rewrite) and nginx location directive.