Redirects

Contents
301, 302, 307, 308
Apache: .htaccess
Two rules for one page
Nginx
Check a redirect
When it does not work

A redirect answers a request for an old address with a status code and the new address in the Location header; the browser then loads the new one. Where you set it depends on the web server: .htaccess on Apache, nginx.conf on Nginx. The outputs on this page come from test containers with Apache 2.4 and Nginx 1.23, checked on 26 October 2026.

301, 302, 307, 308

CodeMeaningUse it for
301Moved permanentlyA page that moved for good. Search engines move its ranking to the new address.
302Found, temporaryA short-time detour; the old address stays the main one.
307Temporary, keep the methodLike 302, but a POST stays a POST.
308Permanent, keep the methodLike 301, but a POST stays a POST.

All codes: HTTP status codes.

Apache: .htaccess

In the .htaccess of the site root, one line per redirect: the old path from the root of the site, then the new address. Redirect belongs to mod_alias and needs no RewriteEngine on:

Redirect 301 /old_url1.php /new_url.php Redirect 301 /old_dir/url1.php /new_dir/url1.php

A rule for a folder moves everything below it and keeps the rest of the path, so /old_dir/a.php goes to /new_dir/a.php:

Redirect 301 /old_dir /new_dir

If the site runs behind a proxy that ends HTTPS, a relative target such as /new_url.php can come back as an http:// address. Why, and the mod_rewrite form that avoids it: .htaccess.

Two rules for one page

A folder moved, and one page in it also got a new name:

/old_dir/a.php → /new_dir/a.php

/old_dir/page.php → /new_dir/new_page.php

Apache uses the first Redirect that matches. With the folder rule first, page.php first goes to /new_dir/page.php, which does not exist, and needs a second rule from there. The visitor makes two hops:

Redirect 301 /old_dir /new_dir Redirect 301 /new_dir/page.php /new_dir/new_page.php

curl -sIL http://127.0.0.1:18080/old_dir/page.php | grep -E '^(HTTP|Location)'

HTTP/1.1 301 Moved Permanently Location: http://127.0.0.1:18080/new_dir/page.php HTTP/1.1 301 Moved Permanently Location: http://127.0.0.1:18080/new_dir/new_page.php HTTP/1.1 200 OK

Put the rule for the single page first, and it is one hop:

Redirect 301 /old_dir/page.php /new_dir/new_page.php Redirect 301 /old_dir /new_dir

HTTP/1.1 301 Moved Permanently Location: http://127.0.0.1:18080/new_dir/new_page.php HTTP/1.1 200 OK

Exact rules before folder rules, in every server.

Banner image

Nginx

Nginx has no .htaccess; the rules go into the server block of the site, see nginx.conf. An exact location wins over a prefix one regardless of the order:

location = /old_url1.php { return 301 /new_url.php; } location = /old_dir/page.php { return 301 /new_dir/new_page.php; } location ^~ /old_dir/ { rewrite ^/old_dir/(.*)$ /new_dir/$1 permanent; }

Nginx turns a relative target into a full address with the host and its own port. Behind a port mapping or a proxy that is the wrong port. The test container listened on 80, mapped to 18081 outside:

curl -sI http://127.0.0.1:18081/old_dir/page.php | grep -E '^(HTTP|Location)'

HTTP/1.1 301 Moved Permanently Location: http://127.0.0.1/new_dir/new_page.php

The port is lost. With absolute_redirect off; in the server block Nginx sends the target as written, and the browser keeps the host and port it used:

HTTP/1.1 301 Moved Permanently Location: /new_dir/new_page.php

Check a redirect

curl shows the first answer only with -I, and follows the whole chain with -L, as in the examples above. Every hop is a Location line; more than one means a chain to shorten.

When it does not work

Read the error log of the site. At most hosting providers it is in the control panel or in a logs folder next to the site; on your own Apache server it is usually /var/log/apache2/error.log. The last 100 lines:

tail -n 100 /var/log/apache2/error.log

A request for a page that no longer exists shows up there with its path; that path is the old address to redirect. A browser also remembers 301 answers: test a changed rule with curl or in a private window.

Article authors: Andrei Olegovich, Klavdiya Kondakova

Related articles
Responsive design: an adaptive menu
Beget mail: create a mailbox
Canonical page: rel=canonical
CSS
Firefox
Which hosting to choose
VPS hosting
HTTP status codes
Joomla
OpenCart
PUT vs POST
Redirects
Russian IP ranges
SEO
Element tbody not allowed as child of element table in this context
CSS test: shapes and line numbers
CSS test 2: the code blocks of this site
Website testing
A tram drawn with CSS
URL encoding
UTM tags
Telling bots from people
WebP images
Moving a website: domain and hosting
WordPress
Move a WordPress site
Yandex Metrica: goals
Yandex Mail: collect mail from other mailboxes

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: