Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The standard PHP redirect sends a Location response header and then stops the script:

<?php
header('Location: /new-page.php');
exit;

This normally returns a temporary 302 Found response. The browser then requests /new-page.php. Use an explicit status code when the move is permanent, follows a form submission, or must preserve the original request method and body.

The correct PHP redirect syntax

PHP does not move a file or redirect by displaying a special page. It sends an HTTP response before the response body begins:

HTTP/1.1 302 Found
Location: /login.php

The client decides whether to follow that Location value. It may be an absolute URL or a site-relative path such as /login.php. PHP’s syntax is:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
header(string $header, bool $replace = true, int $response_code = 0);
  • The first argument is the header, normally Location: ....
  • The second controls replacement of an existing header of the same type.
  • The third explicitly sets the HTTP status.

Put the call before any HTML, echo, debugging output, included template output, warning, or accidental whitespace. A UTF-8 byte-order mark before <?php can also count as output. The PHP manual documents this requirement and the special handling of Location headers at php.net.

header() alone does not stop PHP execution. Always terminate the redirect branch with exit; (or die;) so later code cannot change state or expose a response that the browser will not see.

Choose the right redirect status

Status Meaning Typical use Method and body
301 Permanently moved Permanent page or URL migration Some clients historically change non-GET requests to GET
302 Found; temporary Ordinary temporary browser navigation; PHP’s usual Location default Non-GET behavior can vary
303 See Other Post/Redirect/Get after a successful form or other operation Follow-up request is GET
307 Temporary Redirect Temporary routing that must preserve an upload or API request Preserves method and body
308 Permanent Redirect Permanent routing where method preservation matters Preserves method and body

Use 301 for a permanent ordinary page move, or 308 when a non-GET method must remain unchanged. Use 302 for a temporary ordinary navigation, 303 when an action should lead to a new page fetched with GET, and 307 when the destination must receive the same method and body. A 307 or 308 can repeat a non-idempotent operation, so do not select one casually. Definitions and method rules are summarized by MDN’s redirection guide and its HTTP status reference.

Explicit examples

<?php
// Temporary navigation
header('Location: /home.php', true, 302);
exit;

// Permanent ordinary page move
header('Location: /new-page.php', true, 301);
exit;

// Temporary redirect that preserves method and body
header('Location: https://api.example.com/process', true, 307);
exit;

Google recommends a permanent server-side redirect such as 301 or 308 when a URL has permanently moved and the new URL should replace the old one in search results; it does not guarantee a particular ranking outcome. See Google’s redirect guidance.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Redirect after a form submission

Use the Post/Redirect/Get pattern so refreshing the result page does not submit the form again:

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Validate input, save data, and set any session message.
    header('Location: /thank-you.php', true, 303);
    exit;
}

303 deliberately changes the follow-up request to GET. Choose 307 instead only when the destination must receive the original method and body again.

Redirect conditionally for login or application logic

<?php
session_start();

if (empty($_SESSION['user_id'])) {
    header('Location: /login.php', true, 302);
    exit;
}

// Protected code runs only for an authenticated session.

A browser user can be sent to a login page, but an API normally should return an appropriate 401 or 403 response instead of an HTML login redirect.

Safely preserving a return path

Never place an unchecked destination in Location. Keep the value local (or use an allowlist) to prevent an open redirect that can support phishing:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
$next = $_GET['next'] ?? '/dashboard.php';

if (
    !is_string($next) ||
    $next === '' ||
    $next[0] !== '/' ||
    str_starts_with($next, '//')
) {
    $next = '/dashboard.php';
}

header(
    '/login.php?next=' . rawurlencode($next),
    true,
    302
);
exit;

For sensitive applications, an allowlist of known internal paths is stronger than accepting arbitrary local paths.

Add query parameters correctly

Encode each value, or let http_build_query() construct the query string:

<?php
$userId = 42;
header('/profile.php?id=' . rawurlencode((string) $userId), true, 302);
exit;
<?php
$query = http_build_query([
    'status' => 'success',
    'id' => 42,
]);

header('/result.php?' . $query, true, 303);
exit;

Do not concatenate raw user input into a header. Validate destinations, encode parameter values, use HTTPS for sensitive destinations, and never put credentials or access tokens in a redirect URL.

Redirect to another domain

<?php
header('Location: https://www.example.com/', true, 302);
exit;

External destinations should use an absolute HTTPS URL. If a user chooses the destination, map a short key to approved URLs rather than trusting a submitted URL:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
$allowed = [
    'docs' => 'https://docs.example.com/',
    'support' => 'https://support.example.com/',
];

$key = $_GET['site'] ?? '';
$destination = $allowed[$key] ?? '/';

header('Location: ' . $destination, true, 302);
exit;

filter_var($url, FILTER_VALIDATE_URL) checks syntax, not whether the host is trusted. Do not build redirects from an unvalidated Host header.

Fix “headers already sent”

The error Cannot modify header information - headers already sent means PHP began sending output before the redirect. Typical causes include:

  • HTML, echo, print, or debugging output before header().
  • Whitespace outside PHP tags or a UTF-8 BOM in an included file.
  • A warning, notice, or template that emitted output.
  • Redirect code placed after rendering has started.

Move the redirect decision earlier and remove the premature output:

<?php
if ($completed) {
    header('Location: /done.php', true, 303);
    exit;
}

echo 'Processing...';

For diagnostics, PHP can report whether headers have been sent and where output began:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
if (headers_sent($file, $line)) {
    error_log("Headers already sent in $file on line $line");
}

var_dump(headers_list());

Output buffering may defer output in some configurations, but it is not the dependable fix; find and remove the source of premature output.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Test the actual response

  1. Open browser developer tools, reload the original URL, and inspect the Network entry for its status and Location header.
  2. Inspect every hop with curl:
curl -i https://example.com/old-page.php
curl -IL https://example.com/old-page.php

The response should show the expected status and location, for example:

HTTP/2 301
location: https://example.com/new-page.php

To inspect a POST response without automatically following it:

curl -i -X POST https://example.com/submit.php

Use curl -L when you want the final response after following redirects, not when you need to inspect each individual hop. Confirm that the destination returns the expected final status and that no unnecessary chain exists.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prevent redirect loops and chains

Loops commonly arise when an old and new route point at each other, HTTP-to-HTTPS rules conflict, a login guard protects the login page, trailing-slash rules disagree with framework routes, or a reverse proxy causes PHP to misread the original scheme. A proxy that terminates TLS must be configured so the application can trust the correct forwarded scheme.

Run curl -IL and inspect every hop. Global canonical-host, HTTP-to-HTTPS, and large migration rules are usually safer as one server- or proxy-level rule rather than several application redirects.

PHP versus Apache or Nginx redirects

Use PHP when the destination depends on a session, role, database record, or form result. Use Apache, Nginx, a load balancer, or a CDN when a rule applies to every request and can run before PHP starts, such as HTTP-to-HTTPS, host canonicalization, or a static path migration.

Apache

Redirect 301 /old-page https://example.com/new-page

Nginx

server {
    listen 80;
    server_name example.com;

    return 301 https://www.example.com$request_uri;
}

MDN covers Apache Redirect/mod_rewrite and Nginx return/rewrite alternatives at its redirection guide. Nginx’s redirect and rewrite behavior is documented at nginx.org.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common mistakes to avoid

  • Omitting exit; after a conditional redirect.
  • Calling header() after output has begun.
  • Using 301 for a temporary test, then being confused by cached behavior.
  • Using 302 for a permanent migration.
  • Using 302 or 301 when method preservation is required, instead of 307 or 308.
  • Using a method-preserving redirect after an operation that must not be repeated.
  • Trusting a user-supplied URL or raw query value.
  • Creating redirect chains, loops, or a PHP rule for a site-wide server concern.
  • Replacing an available HTTP redirect with JavaScript or a meta refresh. Those alternatives require the original page to load, may fail with JavaScript disabled, and provide weaker semantics for crawlers and clients.

Frequently Asked Questions

Can PHP redirect to another domain?

Yes. Send an absolute HTTPS URL in the Location header, and use an allowlist when the destination is selected by a user or request parameter.

Does header() stop PHP execution?

No. It sends the response header; add exit; immediately to stop the current script.

Can I redirect before ?

Yes. The redirect must be issued before any output, including the doctype, whitespace, warnings, or included template content.

Can I redirect to a URL without a .php extension?

Yes. The destination can be any valid relative path or absolute URL handled by your server or application.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.