But doing it wrong can cause major headaches. You could create SEO-damaging redirect chains, break your website with “headers already sent” errors, or even open a massive security hole called an “open redirect vulnerability.” This guide will walk you through everything you need to know to create safe, secure, and effective PHP redirects like a professional.

Key Takeaways

  • Always Use Server-Side Redirects: For SEO and user experience, always prefer server-side redirects (like PHP header()) over client-side redirects (like JavaScript or HTML <meta> tags).
  • 301 vs. 302: Use a 301 (Permanent) redirect for content that has moved forever. This tells search engines to pass all SEO value (“link juice”) to the new URL. Use a 302 (Temporary) redirect for short-term moves, like A/B testing or site maintenance, where you intend for the original URL to return.
  • The Golden Rule: header() + exit(): The correct way to perform a PHP redirect is header(“Location: new-page.php”); exit;. The exit; or die(); call is not optional. It stops the script from executing further, which prevents potential errors and security issues.
  • The “Headers Already Sent” Error: You cannot use the header() function after any output has been sent to the browser. This includes HTML, whitespace before your <?php tag, or echo statements.
  • Never Trust User Input: The single biggest security risk is using user-supplied data (like $_GET[‘url’]) directly in a redirect. This creates an “Open Redirect” vulnerability. Always validate user input against a strict whitelist of approved URLs.
  • WordPress & Elementor: In WordPress, you should use hooks like template_redirect to fire redirects. Better yet, a tool-based approach like the Redirect Manager in Elementor Pro abstracts this complexity, allowing you to manage 301s, 302s, and 404s from a simple UI without touching code.

Chapter 1: The “Why” – Understanding HTTP Redirects

Before we write a single line of code, let’s understand what’s happening under the hood. A redirect is a conversation between the web server and the web browser.

  1. Browser Requests: A user types http://example.com/old-page into their browser or clicks a link. The browser sends an HTTP GET request to your server for /old-page.
  2. Server Responds: Your PHP script runs. Instead of sending back HTML content, it sends a special HTTP response header. This header tells the browser, “The content you want isn’t here. It’s over there.”
  3. Browser Obeys: The browser reads this header, takes note of the new URL, and immediately sends a new request to the new location.

The key part of this “conversation” is the HTTP status code the server sends. These codes are crucial for SEO and browser behavior.

The Critical Redirect Status Codes

You have a few codes to choose from. Picking the right one is essential.

301: Moved Permanently

This is the most common and important redirect. It tells browsers and search engines, “This page is gone forever. All future requests for this URL should go to the new one. Please update your records.”

  • SEO: This is the big one. A 301 redirect passes the vast majority of search ranking power (or “link equity”) from the old page to the new one.
  • Browser: Browsers will cache a 301 redirect very aggressively. If you make a mistake, it can be hard for users to see the fix because their browser will just keep redirecting to the wrong place.
  • Use Cases:
    • You renamed a page (e.g., /contact-us.php to /contact.php).
    • You changed your site structure (e.g., /blog/my-post to /articles/my-post).
    • You migrated your entire website to a new domain.
    • You are forcing www to non-www or HTTP to HTTPS.

302: Found (or Temporary Redirect)

This code says, “I found the document, but it’s temporarily at this other location. Please go there for now, but keep using the original URL for future requests.”

  • SEO: No significant link equity is passed. Search engines understand this is temporary and will keep the original page indexed.
  • Browser: Browsers do not cache this redirect. They will check the original URL every time.
  • Use Cases:
    • A/B testing a new landing page.
    • Redirecting users based on their location or language.
    • A “coming soon” or site maintenance page.
    • Redirecting a user after a form submission.

307: Temporary Redirect

This is the modern, more specific version of a 302. The key difference is that a 307 guarantees that the request method will not change. If the user sent a POST request (like a form submission) to the original URL, the browser will send a POST request to the new one. A 302 doesn’t offer this guarantee (though most modern browsers behave this way).

  • Use Case: The best choice for redirecting after a POST form submission if you need the data to be re-posted to the new location.

308: Permanent Redirect

This is the permanent version of a 307. It’s a 301 redirect that guarantees the request method will not change.

  • Use Case: A rare but useful case where you permanently move a resource that accepts POST or PUT requests and you want future requests of that type to go to the new URL.

For 99% of your work, you will be choosing between 301 (Permanent) and 302 (Temporary).

Chapter 2: The “How” – Using the PHP header() Function

The one and only function for this job in PHP is header().

The basic syntax is: header(“Location: http://example.com/new-page.php”);

This looks simple, but it’s loaded with traps. Let’s build the correct, safe redirect brick by brick.

Trap #1: The “Headers Already Sent” Error

This is the most common error every PHP developer hits.

What it means: You cannot send an HTTP header after you have sent any other output to the browser. Why? Because the headers are the very first part of the HTTP response. The body (your HTML, your echo text) comes after.

Common causes:

Whitespace: A single space or blank line before your opening <?php tag.

<?php

// This will fail! There is a blank line above.

header(“Location: new-page.php”);

?>

HTML: Any HTML content before your PHP block.
<!DOCTYPE html>

<html>

<head>

    <?php

    // This will fail! The HTML above is output.

    header(“Location: new-page.php”);

    ?>

</head>

<body>…</body>

</html>

PHP Output: Any echo, print, or printf statements.
<?php

echo “Processing…”;

// This will fail! “Processing…” has already been sent.

header(“Location: new-page.php”);

?>

How to Fix It (The Pro Way): Always put your redirect logic at the very top of your file, before any other logic or output.

<?php

// This is the

// very first

// thing in the file.

// No spaces, no HTML.

if ( some_condition() ) {

    header(“Location: new-page.php”);

    exit;

}

// All other page logic and HTML comes *after*.

?>

<!DOCTYPE html>

<html>

</html>

How to Fix It (The “Duct Tape” Way): Output Buffering Sometimes, you’re in a complex application and can’t control when output starts. In these cases, you can use output buffering. ob_start() tells PHP to “hold on to all output in memory” instead of sending it. ob_end_flush() (or ob_end_clean()) then sends it (or discards it).

<?php

ob_start(); // Start the output buffer

// … some code …

echo “This is fine now.”;

// … more code …

// This will work, because the buffer is holding the “echo”

// and hasn’t sent it to the browser yet.

header(“Location: new-page.php”);

ob_end_flush(); // Not strictly needed if you exit

exit;

?>

While this works, it’s better to structure your code to avoid this.

Trap #2: Forgetting to exit;

This is a critical security and stability mistake.

What it means: When you call header(), you are just scheduling a header to be sent. The PHP script keeps running to the very end.

Why this is bad:

  1. Code Execution: If you have code after the redirect, it will still run. This could be anything from sending an email to deleting data.
  2. Security: If the redirect is conditional, the rest of your page might render and be sent to the browser along with the redirect header. A user might not see it, but a malicious bot could.

The Golden Rule: Always, always, always call exit; or die(); immediately after your header() call.

<?php

// THE CORRECT, SAFE WAY

if ( !is_user_logged_in() ) {

    // 1. Send the redirect header

    header(“Location: [http://example.com/login.php](http://example.com/login.php)”);

    // 2. STOP THE SCRIPT.

    exit;

}

// This code will now *only* run if the user is logged in.

echo “Welcome to the members-only area!”;

?>

Trap #3: Not Sending the Right Status Code

By default, a header(“Location: …”) call sends a 302 (Temporary) redirect. This is often not what you want, especially for SEO.

You need to be explicit. You can do this by sending a second header() call or by using the function’s optional parameters.

The Full, Correct Syntax: header(string $header, bool $replace = true, int $response_code = 0): void

  • $header: Your “Location: …” string.
  • $replace: Keep this true. It just means this header replaces any previous “Location” header.
  • $response_code: This is the magic number.

Example: A Correct 301 (Permanent) Redirect

<?php

// Send a permanent 301 redirect

header(“Location: [http://example.com/new-permanent-page.php](http://example.com/new-permanent-page.php)”, true, 301);

exit;

?>

Example: A Correct 302 (Temporary) Redirect

<?php

// Send a temporary 302 redirect

// You can just use the default, or be explicit.

header(“Location: [http://example.com/temporary-page.php](http://example.com/temporary-page.php)”, true, 302);

// Or simply: header(“Location: [http://example.com/temporary-page.php](http://example.com/temporary-page.php)”);

exit;

?>

Chapter 3: Creating a “Safe” PHP Redirect Function

Now that we know the pitfalls, let’s build a reusable, safe function. This is a best practice that will save you a lot of trouble.

<?php

/**

 * Safely redirects to a new URL.

 *

 * @param string $url The URL to redirect to.

 * @param int $status_code The HTTP status code to use (e.g., 301 or 302).

 */

function safe_redirect($url, $status_code = 302) {

    // Make sure headers haven’t already been sent

    if (headers_sent()) {

        // Optional: Log an error here for debugging

        // error_log(“Redirect failed: Headers already sent.”);

        return;

    }

    // Sanity check on the URL

    // A basic filter to ensure it’s a plausible URL.

    // This does NOT prevent open redirect. See Chapter 4.

    if (!filter_var($url, FILTER_VALIDATE_URL) && !preg_match(‘/^\/[a-zA-Z0-9\-\_\/]*$/’, $url)) {

         // Optional: Log an error

         // error_log(“Redirect failed: Invalid URL provided: ” . $url);

         return;

    }

    // Send the redirect

    header(“Location: ” . $url, true, $status_code);

    // Stop the script

    exit;

}

// — HOW TO USE IT —

// Permanent 301 Redirect

safe_redirect(“[https://example.com/new-home.php](https://example.com/new-home.php)”, 301);

// Temporary 302 Redirect (default)

safe_redirect(“/some-local-page.php”);

?>

This function is better. It checks headers_sent() and it always calls exit. But it’s still missing one giant piece of the “safe” puzzle.

Chapter 4: The #1 Security Risk – Open Redirect Vulnerabilities

This is the “safely” part of the article title. Please read this section carefully.

An Open Redirect is a vulnerability where your website’s code takes a URL from a user and redirects to it… without checking what that URL is.

Vulnerable Code (DO NOT USE):

<?php

// A user visits: [http://example.com/redirect.php?url=http://malicious-site.com](http://example.com/redirect.php?url=http://malicious-site.com)

// This code will happily redirect them to the malicious site.

$redirect_to = $_GET[‘url’];

safe_redirect($redirect_to); // Our function from Chapter 3 is still vulnerable!

?>

Why is this so bad? This is a powerful phishing tool. A hacker can craft a link that looks like it’s from your trusted website, but it redirects the user to a perfect clone of your site that they control.

http://your-trusted-bank.com/login-redirect.php?url=http://your-trusted-bank.co

The user sees your domain, clicks the link, and gets redirected to a phishing site. They enter their login, and the hacker has their credentials.

How to Prevent Open Redirects: The Whitelist

The only truly safe way to handle user-supplied redirect URLs is to validate them against a whitelist of approved destinations.

Safe Code (The Whitelist Approach):

<?php

// 1. Define your whitelist of allowed redirect *keys*.

$allowed_destinations = [

    “home” => “/”,

    “profile” => “/user/profile.php”,

    “dashboard” => “/admin/dashboard.php”,

    “google” => “[https://google.com](https://google.com)”

];

// 2. Get the *key* from the user, not the full URL.

// User visits: [http://example.com/redirect.php?dest=profile](http://example.com/redirect.php?dest=profile)

$destination_key = $_GET[‘dest’];

// 3. Check the key against the whitelist

if ( isset($destination_key) && array_key_exists($destination_key, $allowed_destinations) ) {

    // 4. The key is safe. Get the *actual* URL from our secure list.

    $url = $allowed_destinations[$destination_key];

    // Now we can safely redirect

    safe_redirect($url, 302);

} else {

    // 5. If the key is not on our list, send them to a safe default.

    safe_redirect(“/index.php”, 302);

}

?>

This is bulletproof. The user can only provide a simple string, and you control the mapping to the actual URL.

A “Good Enough” Fix: Local-Only Redirects

Sometimes a whitelist is too restrictive. You might have a login.php page that needs to redirect back to whatever page the user was just on. In this case, you can enforce local-only redirects.

<?php

// User visits: [http://example.com/login.php?return_to=/my-awesome-post.php](http://example.com/login.php?return_to=/my-awesome-post.php)

$return_to = $_GET[‘return_to’];

// Check if $return_to is a valid *local* path.

// 1. It must start with a single ‘/’.

// 2. It must NOT start with ‘//’ (this is a protocol-relative link vulnerability).

// 3. It must NOT contain ‘:’ (to prevent `javascript:alert(1)`).

if ( !empty($return_to) && $return_to[0] == ‘/’ && (strlen($return_to) > 1 && $return_to[1] != ‘/’) && strpos($return_to, ‘:’) === false ) {

    // It looks like a safe, local path.

    // Let’s build the full URL to be safe.

    $host = $_SERVER[‘HTTP_HOST’];

    $url = “https://” . $host . $return_to; // Force HTTPS

    safe_redirect($url, 302);

} else {

    // Default to home page

    safe_redirect(“/index.php”, 302);

}

?>

This is much safer. It prevents redirects to external domains.

Chapter 5: Advanced PHP Redirect Techniques

Passing Query Parameters

What if you need to forward the query string (?foo=bar&baz=123) to the new URL?

<?php

// User visits: /old-page.php?id=123&source=email

$new_url = “/new-page.php”;

if ( !empty($_SERVER[‘QUERY_STRING’]) ) {

    $new_url .= “?” . $_SERVER[‘QUERY_STRING’];

}

// $new_url is now “/new-page.php?id=123&source=email”

safe_redirect($new_url, 301);

?>

Building a New Query String

Need to add your own query parameters? Use http_build_query().

<?php

$params = [

    ‘user_id’ => 123,

    ‘message’ => ‘welcome’,

    ‘from’ => ‘redirect’

];

// This safely builds: “user_id=123&message=welcome&from=redirect”

$query_string = http_build_query($params);

$new_url = “/welcome.php?” . $query_string;

safe_redirect($new_url, 302);

?>

Chapter 6: Redirects in a WordPress & Elementor World

If you’re using a Content Management System (CMS) like WordPress, things are a little different. You can’t just put a header() call at the top of your page.php template. Why? Because by the time that file is loaded, WordPress has already sent tons of output (the <html> and <head> tags).

The WordPress Way: template_redirect Hook

WordPress provides special “hooks” to run code at the right time. The correct hook for redirects is template_redirect, which runs just before WordPress determines which template to load, but after it knows what post or page is being requested.

You would add this code to your theme’s functions.php file:

<?php

// in functions.php

add_action(‘template_redirect’, ‘my_custom_redirects’);

function my_custom_redirects() {

    // Redirect a specific old page

    // is_page() checks if we are on a specific Page (by ID or slug)

    if ( is_page(‘old-about-page’) ) {

        // WordPress has its own safe redirect function

        wp_redirect(‘[https://example.com/new-about-page](https://example.com/new-about-page)’, 301);

        // And it *requires* an exit call!

        exit;

    }

    // Redirect an old blog post

    if ( is_singular(‘post’) && get_the_ID() == 123 ) {

        wp_redirect(‘[https://example.com/new-blog-post-url](https://example.com/new-blog-post-url)’, 301);

        exit;

    }

}

?>

wp_redirect() is just a wrapper for the PHP header() function that also helps set the status code. You still must exit.

The Easy & Smart Way: Managing Redirects in Elementor

Let’s be honest. Editing functions.php is slow, technical, and risky. What if you make a typo? You could take down your whole site. And how do you keep track of all the redirects you’ve made?

This is where a tool-based approach is superior. If you are building your site with Elementor, the Redirect Manager in Elementor Pro handles all of this for you.

Instead of writing code, you just use a simple UI:

  • Go to Elementor > Redirects.
  • Click “Add New.”
  • Source URL: Type in the old URL (/old-about-page).
  • Target URL: Type in the new URL (/new-about-page).
  • Redirect Type: Choose 301, 302, or 307 from a dropdown.
  • Hit “Save.”

The benefits of this are massive:

  1. No Code, No Risk: You can’t cause a “headers already sent” error or a PHP fatal error.
  2. It’s “Safe” By Default: The UI handles everything. There is no risk of an open redirect.
  3. 404 Error Tracking: Elementor also logs all your 404 “Page Not Found” errors. You can look at the list, see a page that’s getting a lot of 404 hits, and click a “Redirect” button right there to fix it. This is a huge win for both UX and SEO.
  4. Wildcards: You can redirect entire sections, like /blog/2020/* to /archives/*.
  5. Central Management: You have one single screen to see and manage all your redirects.

For any serious WordPress site builder, managing redirects at the code level is a last resort. A professional, integrated tool is faster, safer, and more scalable.

Chapter 7: Other Redirect Methods (And When to Use Them)

PHP isn’t the only way. For a complete picture, here are your other options.

1. .htaccess (Apache Server)

This is a configuration file on your server. It’s extremely fast because it runs before PHP even wakes up. It’s the best choice for “global” redirects, like forcing HTTPS or www.

Example: Redirect a single page

# In your .htaccess file

Redirect 301 /old-page.html /new-page.html

Example: Force HTTPS

# In your .htaccess file

RewriteEngine On

RewriteCond %{HTTPS} off

RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

  • Pros: Extremely fast, powerful (with RewriteRule).
  • Cons: Confusing syntax, server-specific (won’t work on Nginx), a typo can take down your whole site.

2. Client-Side Redirects (The “Avoid These” Group)

These redirects happen in the user’s browser, not on the server.

JavaScript Redirect: window.location.href = “http://example.com/new-page.js”;

HTML <meta> Refresh: <meta http-equiv=”refresh” content=”5;url=http://example.com/new-page.html”> (This waits 5 seconds, then redirects).

Why You Must Avoid These for SEO:

  • No Link Equity: Search engines do not reliably pass any SEO value. They might eventually figure it out, but you are not telling them it’s a 301.
  • Bad User Experience: The user’s browser loads the old page, then it starts to load the new one. It’s slow and jarring.
  • Breaks the “Back” Button: A meta refresh, in particular, can make the back button un-usable.
  • Accessibility Nightmare: As my colleague, web creation expert Itamar Haim, often points out, “Relying on client-side redirects is a gamble with both your SEO and your user’s trust. A proper server-side 301 is always the professional’s choice.” A meta refresh can be disorienting and confusing, especially for users with assistive technologies.

The only time to use a JS redirect is after a user action, like clicking “Submit” on a form where you’ve just shown them an “Success!” message.

Chapter 8: Testing & Debugging Your Redirects

You’ve set up your redirect. How do you know it’s working and sending the right status code?

Do NOT trust your browser’s address bar. Your browser might be showing you a cached redirect.

Method 1: Browser Dev Tools (The Best Way)

  1. Open your browser (Chrome, Firefox).
  2. Open Developer Tools (F12 or Ctrl+Shift+I).
  3. Go to the “Network” tab.
  4. Click the “Disable cache” checkbox. This is critical.
  5. Type in your old URL and hit Enter.
  6. You will see two entries. The first one is your old URL.
  7. Click it. In the “Headers” panel, you will see your redirect code.
  • Status Code: 301 Moved Permanently (or 302, 307, etc.)
  • Response Headers: Location: https://example.com/new-page.html

If you see that, it worked perfectly.

Method 2: Online Redirect Checker

Google “HTTP redirect checker.” These sites will follow your redirect chain and tell you exactly what’s happening. They are great because they are not affected by your browser’s cache.

Method 3: curl (Command Line)

For the pros, curl is the fastest way. Open your terminal and type: curl -I http://example.com/old-page.php

The -I flag just asks for the headers. The output will be:

HTTP/1.1 301 Moved Permanently

Date: Mon, 17 Nov 2025 23:30:10 GMT

Server: Apache

Location: [http://example.com/new-page.php](http://example.com/new-page.php)

Content-Type: text/html; charset=UTF-8

This is a clean, definitive test.

Fixing “ERR_TOO_MANY_REDIRECTS”

This is a redirect loop. It’s when Page A redirects to Page B, and Page B redirects back to Page A.

A -> B -> A -> B -> …

The browser gives up and shows this error. Use one of the testing tools above to trace the “chain” of redirects. You will see the loop. The fix is to find the code (or .htaccess rule, or Elementor redirect setting) that is causing the second redirect and remove it.

Final Thoughts

A PHP redirect is a simple header() call, but a safe, effective, and professional redirect requires more.

  • It must be at the top of your file.
  • It must be followed by exit;.
  • It must use the correct status code (301 or 302).
  • It must never, ever trust user input without a whitelist.

For small scripts, a safe_redirect() function is a great tool. For larger, CMS-driven websites, handling redirects at the code level is inefficient and risky. A modern web creation platform integrates this functionality. Tools like the Elementor Pro Redirect Manager are the modern, safe, and scalable solution. They let you focus on building great experiences, knowing your site’s technical foundation is solid.

Frequently Asked Questions (FAQ)

1. What’s the real difference between a 301 and 302 redirect?

  • 301 (Permanent): Tells search engines the move is forever. All SEO value from the old page should be transferred to the new page. The browser will cache this aggressively.
  • 302 (Temporary): Tells search engines this is a short-term move. The old page should remain indexed, and no SEO value is passed. The browser will not cache this.

2. Why do I get a “headers already sent” error in PHP? You are trying to use the header() function after some output has already been sent to the browser. This can be an echo statement, HTML, or even a single space or blank line before your opening <?php tag. Put your redirect logic at the very top of your file.

3. Is it okay to use a JavaScript redirect? You should avoid it for permanent page moves. Search engines do not treat window.location.href as a 301 redirect, so you will lose your SEO value. It’s also a slower, jarring experience for the user. Only use it for actions after a user has interacted with the page (like submitting a form).

4. How do I pass query parameters in a PHP redirect? To forward the existing query string, append $_SERVER[‘QUERY_STRING’] to your new URL. To build a new query string, create an array of parameters and run it through the http_build_query() function.

5. What is an “open redirect vulnerability”? This is a major security flaw where your code uses user input (like $_GET[‘url’]) to define the redirect destination. A hacker can use this to create a phishing link that uses your domain to redirect users to a malicious domain. Always validate user-supplied redirect data against a strict whitelist.

6. How do I fix a redirect loop (“ERR_TOO_MANY_REDIRECTS”)? A redirect loop means Page A points to Page B, and Page B points back to Page A. Use your browser’s Network tab or an online redirect checker to trace the redirect chain. Once you find the two (or more) rules that are conflicting, remove or fix one of them.

7. Which is better: PHP redirect or .htaccess redirect?

  • For global, site-wide rules (like forcing HTTPS or www), .htaccess is faster and more efficient as it runs before PHP.
  • For conditional, logic-based redirects (like “redirect if user is not logged in”), PHP is the only choice.
  • For day-to-day page moves (like renaming a blog post), a plugin or tool like Elementor’s Redirect Manager is the safest and easiest solution.

8. Do redirects hurt SEO? No! When used correctly, they are essential for good SEO. A 301 redirect is the correct way to tell search engines that a page has moved, and it preserves your search rankings by passing link equity to the new page. Not using a redirect (and just letting the old page become a 404) is what hurts your SEO.

9. How can I test that my redirect is a 301 and not a 302? You cannot rely on your browser’s address bar. You must use your browser’s Developer Tools (F12), go to the “Network” tab (with “Disable cache” checked), and look at the “Status” column for your old URL. It will explicitly say 301 or 302.

10. How do I handle redirects in WordPress without coding? The safest and most powerful way is to use an integrated tool. The Redirect Manager in Elementor Pro allows you to create, manage, and delete 301, 302, and 307 redirects from a simple dropdown menu. It also automatically tracks your 404 errors so you can find and fix broken links easily.