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 .conf file 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

  1. Back up the file: sudo cp /etc/nginx/sites-available/example.com /root/example.com.bak
  2. Add the redirect (examples below) and save.
  3. Test the syntax: sudo nginx -t. It must end with test is successful.
  4. Reload without dropping connections: sudo systemctl reload nginx
  5. 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; answered 302 Moved Temporarily. According to nginx, return with 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; answered 308 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 with curl: (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 -t answered [emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:44 and test 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 /offers as 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.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Managed dedicated servers

our engineers look after the server for you, from $135 a month.

Was this answer helpful?

Still stuck? Open a ticket