A 301 redirect is a server-level instruction that permanently forwards one URL to another. The 301 status code tells browsers and search engines that the page has moved permanently – not temporarily – which means search engines transfer the original page's link equity to the destination URL and update their index to reflect the new address. It is the correct method for URL changes during site migrations, slug corrections, and domain consolidations where you want SEO value preserved.
For shared hosting, cPanel, and most traditional web servers
Apache is the most common web server for shared hosting. If you are on cPanel, Plesk,
or similar, your redirects go in a file called .htaccess in your site's
document root. Make sure the mod_rewrite module is enabled – it is on
virtually all shared hosts by default.
Redirect a single page:
Redirect 301 /old-page/ https://yoursite.com/new-page/
Redirect an entire directory:
RedirectMatch 301 ^/old-section/(.*)$ https://yoursite.com/new-section/$1
Redirect a full domain (www to non-www):
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.yoursite\.com [NC]
RewriteRule ^(.*)$ https://yoursite.com/$1 [R=301,L]
The [R=301,L] flags mean: send a 301 response (R=301)
and stop processing further rewrite rules (L).
Always include L to prevent the request from matching additional rules below it.
For VPS, cloud servers, and dedicated servers running Nginx
Nginx does not use .htaccess. Redirects go in the server block configuration,
typically in /etc/nginx/sites-available/yoursite.conf or the main
nginx.conf.
After editing, run nginx -t to test syntax before reloading.
Redirect a single page:
server {
listen 80;
server_name yoursite.com;
location = /old-page/ {
return 301 https://yoursite.com/new-page/;
}
}
Redirect www to non-www:
server {
listen 80;
server_name www.yoursite.com;
return 301 https://yoursite.com$request_uri;
}
server {
listen 80;
server_name yoursite.com;
# ... rest of config
}
The return 301 directive is cleaner and faster than using
rewrite for simple redirects. Use
$request_uri to preserve the path and query string in the destination URL.
After editing your config, run sudo systemctl reload nginx for the changes to take effect.
For application-level redirects within PHP scripts
PHP redirects are handled at the application layer using header().
This is the right approach when the redirect decision depends on application logic – for
example, redirecting based on a URL slug pattern, a database lookup, or request parameters.
The critical rule: header() must be called before any output is sent to the browser.
Any echo, print, whitespace, or byte-order mark before header() will silently
break the redirect on most PHP configurations.
Basic 301 redirect in PHP:
<?php
header('Location: https://yoursite.com/new-page/', true, 301);
exit;
Conditional redirect based on URL:
<?php
$redirects = [
'/old-page/' => '/new-page/',
'/old-service/' => '/services/new-service/',
];
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (isset($redirects[$request_uri])) {
header('Location: https://yoursite.com' . $redirects[$request_uri], true, 301);
exit;
}
The third parameter to header() is the HTTP status code. Without it,
PHP sends a 302 by default. Always specify 301 explicitly for permanent
redirects and always call exit; immediately after to stop script execution.
Clean method using functions.php or custom code
WordPress handles many redirects automatically (old slugs, category pages, attachment URLs),
but for specific custom redirects you have two code-based options that do not require
a plugin: WordPress's built-in wp_redirect() function
inside a hook, or editing the site's .htaccess file directly
(since WordPress sites run on Apache in most shared hosting environments).
Using wp_redirect() in functions.php (child theme only):
add_action('template_redirect', function () {
$redirects = [
'/old-page/' => '/new-page/',
'/old-post/' => '/category/new-post/',
];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (isset($redirects[$path])) {
wp_redirect(home_url($redirects[$path]), 301);
exit;
}
});
Or add directly to .htaccess above the WordPress block:
# Custom 301 redirects
Redirect 301 /old-page/ https://yoursite.com/new-page/
# BEGIN WordPress
# ... WordPress .htaccess block below this line
Add custom redirects above the # BEGIN WordPress line in .htaccess.
WordPress regenerates its section of .htaccess occasionally (on permalink setting changes),
so anything inside the WordPress block will be overwritten. Custom rules above it
are safe from that.
Never trust your browser to test a redirect correctly. Browsers cache redirects aggressively, so a cached 301 will look correct even if the server configuration is broken. Use one of these methods instead.
curl from a terminal (most reliable):
curl -I https://yoursite.com/old-page/
The -I flag fetches headers only without following redirects.
A working 301 returns HTTP/1.1 301 Moved Permanently and a
location: header with the destination URL.
curl following the full redirect chain:
curl -IL https://yoursite.com/old-page/
Adding -L makes curl follow redirects and print headers for each hop.
This is useful for checking redirect chains: you should see the 301 headers and then a
final 200 at the destination. If you see multiple 301s, you have a chain to fix.
After deploying a redirect, also check it in Google Search Console under Coverage to confirm Google has picked up the canonical URL change. This can take several days to weeks depending on crawl frequency for your site.
FAQ
Yes. A 301 redirect passes the majority of link equity (also called PageRank) from the old URL to the new one. Google has confirmed that 301 redirects are the correct method for permanent URL changes and that link equity passes through them. The transfer is not instant and can take weeks to fully reflect in rankings. Use 301 for any permanent change where you want SEO value preserved.
A 301 redirect signals a permanent move. It tells browsers and search engines to update their records and stop using the old URL. A 302 redirect signals a temporary move, meaning search engines keep the old URL indexed and do not pass link equity to the new one. Use 301 for permanent URL changes, site migrations, and consolidating duplicate content. Use 302 only when you genuinely plan to restore the original URL, such as during A/B testing or temporary maintenance pages.
The most reliable method is curl with the -I flag from a terminal: curl -I https://olddomain.com/old-page/ — this returns the raw HTTP response headers. A working 301 redirect shows HTTP/1.1 301 Moved Permanently with a Location header containing the new URL. You can also use online tools like the Redirect Checker or Redirect Path browser extension to test and visualize the redirect chain. Always test with the actual URL in incognito mode rather than relying on browser cache.
Yes, and you should pick one canonical version and redirect consistently to it. If your site is non-www (example.com), redirect www.example.com to example.com with a 301. If your site is www (www.example.com), redirect example.com to www.example.com. Both are equally valid for SEO — what matters is consistency. Also set your preferred domain version in Google Search Console. The redirect and the Search Console setting should agree.
Keep redirect chains to a maximum of two hops. A chain is when URL A redirects to URL B which redirects to URL C. Each hop adds latency, and search engine crawlers may not follow long chains reliably — link equity drops with each additional redirect. If you have existing chains from a previous migration, resolve them: update the origin to point directly to the final destination so A redirects straight to C, bypassing B entirely.
About the author
Farman Rind
SEO and GEO consultant with 7+ years running search visibility campaigns across 50+ websites. Farman handles technical SEO including URL structure, redirect audits, and site migrations as part of ongoing client work. Full background →
Technical SEO
Site migrations, redirect audits, and technical SEO fixes. If you are moving a site, consolidating URLs, or cleaning up a broken redirect structure, I can run the full audit and implementation.