How to Redirect a URL (301, 302, Meta, JS)

Complete guide to implementing redirects: server-side (Apache .htaccess, Nginx, Cloudflare), HTML meta refresh, JavaScript, and framework-specific methods. When to use each type.

There are many ways to redirect a URL. Some are correct. Some work but hurt your SEO. Some are hacks that will break eventually. Here is how to implement each type of redirect, when to use it, and when to avoid it.

The Right Way: Server-Side Redirects

Server-side redirects return a 3xx HTTP status code before any page content loads. Search engines understand them. Browsers follow them instantly. This is the correct default.

Apache (.htaccess)

The most common method for shared hosting and WordPress sites. Add rules to the .htaccess file in your site root.

Single URL redirect:

# 301 — Permanent redirect
Redirect 301 /old-page https://example.com/new-page

# 302 — Temporary redirect
Redirect 302 /promo https://example.com/current-sale

Pattern-based redirects with mod_rewrite:

RewriteEngine On

# Redirect entire directory
RewriteRule ^blog/(.*)$ /articles/$1 [R=301,L]

# Force HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

# Force www
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

# Redirect with query string
RewriteCond %{QUERY_STRING} ^id=123$
RewriteRule ^product\.php$ /products/widget? [R=301,L]

The [L] flag matters

The [L] flag means "last rule" — stop processing after this match. Without it, subsequent rules may trigger and create unexpected chains or loops.

Nginx

Nginx handles redirects in the server configuration file, not in a per-directory file like Apache.

Single URL redirect:

server {
    listen 80;
    server_name example.com;

    # Single redirect
    location = /old-page {
        return 301 https://example.com/new-page;
    }

    # Redirect entire directory
    location /blog/ {
        rewrite ^/blog/(.*)$ /articles/$1 permanent;
    }

    # Force HTTPS (entire server block)
    return 301 https://$host$request_uri;
}

Regex-based redirects:

# Redirect old product URLs to new format
location ~ ^/product\.php {
    if ($arg_id = "123") {
        return 301 /products/widget;
    }
}

Nginx requires reload

After editing Nginx config, run nginx -t to test, then systemctl reload nginx to apply. Unlike .htaccess, changes do not take effect automatically.

Cloudflare Page Rules and Redirect Rules

If you use Cloudflare, you can add redirects without touching your server at all.

Page Rules (legacy):

  1. Go to Rules > Page Rules
  2. Enter the URL pattern: example.com/old-page*
  3. Add setting: Forwarding URL
  4. Choose 301 or 302
  5. Enter the destination: https://example.com/new-page

Redirect Rules (newer):

  1. Go to Rules > Redirect Rules
  2. Create a rule with a matching expression
  3. Set the URL redirect action with status code and target

Cloudflare redirects execute at the edge — before the request reaches your server. They are fast and reduce origin load.

Node.js / Express

const express = require('express');
const app = express();

// Single redirect
app.get('/old-page', (req, res) => {
  res.redirect(301, '/new-page');
});

// Pattern redirect
app.get('/blog/:slug', (req, res) => {
  res.redirect(301, `/articles/${req.params.slug}`);
});

Next.js

In next.config.js:

module.exports = {
  async redirects() {
    return [
      {
        source: '/old-page',
        destination: '/new-page',
        permanent: true, // 301
      },
      {
        source: '/blog/:slug',
        destination: '/articles/:slug',
        permanent: true,
      },
    ];
  },
};

Trace your redirect chains

Find redirect loops, broken chains, and unnecessary hops instantly.

The Acceptable Way: Meta Refresh

HTML meta refresh redirects work without server access. They are not ideal, but they work in a pinch.

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="refresh" content="0;url=https://example.com/new-page">
  <title>Page Moved</title>
</head>
<body>
  <p>This page has moved. <a href="https://example.com/new-page">Click here</a> if not redirected.</p>
</body>
</html>

The content="0" means redirect immediately (0 seconds delay).

SEO implications

Google processes meta refresh redirects, but they do not pass link equity as reliably as 301 server-side redirects. Use them only when you cannot modify server configuration — on free hosting, GitHub Pages (without custom routing), or platforms that limit server access.

The Last Resort: JavaScript Redirect

JavaScript redirects execute in the browser after the page loads. They are the weakest form of redirect.

<script>
  window.location.replace("https://example.com/new-page");
</script>

Use window.location.replace() instead of window.location.href =replace() does not add an entry to the browser history, which prevents the back button from trapping users in a redirect loop.

When JavaScript redirects are acceptable:

  • Client-side routing in SPAs (React Router, Vue Router)
  • Conditional redirects based on client-side state (locale detection, A/B test assignment)
  • Platforms where you have zero server control and cannot use meta refresh

When JavaScript redirects are wrong:

  • Permanent page moves (use 301)
  • SEO-important pages (search engines may not execute JS reliably)
  • Anything that a server-side redirect can handle

When to Use Each Status Code

CodeNameWhen to UseSEO
301Moved PermanentlyPage has permanently moved to a new URLPasses link equity
302Found (Temporary)Page temporarily at a different URLMay not pass link equity
307Temporary RedirectSame as 302 but preserves HTTP method (POST stays POST)Same as 302
308Permanent RedirectSame as 301 but preserves HTTP methodSame as 301

Rule of thumb: If the old URL will never come back, use 301. If it will come back (maintenance, seasonal content, A/B testing), use 302.

Common Redirect Mistakes

Using 302 when you mean 301

This is the most common mistake. If a page has permanently moved, use 301. Using 302 tells search engines the old URL might come back, so they may not transfer ranking signals to the new URL.

Forgetting trailing slashes

/about and /about/ are different URLs. If your server treats them differently, you need explicit rules for both. Many redirect loops come from trailing slash inconsistencies.

Creating redirect chains

Redirect A to B. Later, redirect B to C. Now A goes to B goes to C — a chain. Always redirect directly to the final destination.

Redirecting to a page that also redirects

Always verify your redirect destination is a real, live page that returns 200. Not another redirect. Not a 404.

Not updating internal links

After setting up a redirect, update all internal links to point to the new URL directly. Redirects are for external links and bookmarks you cannot control.

Verify Your Redirect

After implementing any redirect, verify it:

# Check the redirect chain
curl -sIL "https://example.com/old-page" | grep -iE "^(HTTP/|location:)"

# Expected output for a clean 301:
# HTTP/2 301
# location: https://example.com/new-page
# HTTP/2 200

One redirect, one 200. Clean.


Server-side 301 for permanent moves. Everything else is a compromise.

Never miss a broken redirect

Trace redirect chains and detect issues before they affect your users and SEO. Free instant tracing.