PHP redirects: how to redirect

PHP redirects: how to redirect

If you work with PHP, sooner or later you will need to send a visitor from one URL to another: after saving a form, when a page changes address, or when you want to protect a private area of your site. That action is called a redirect, and it is one of the most common tasks in web development. In this article we show you how to do a redirect in PHP the right way: what a redirect is, what it is used for, how to use header() with Location, the difference between a 301 and a 302, and the most common mistakes to avoid. Let us go straight to the point, with clear explanations and code examples you can run as they are. If you only want the short answer: in PHP you make a redirect by sending the HTTP Location header with the header() function and stopping execution with exit. But, as with everything related to HTTP, the details matter: when you call the function, the status code that goes with it and how you handle the script output are the difference between a redirect that works and an annoying headers already sent warning on screen.

What is a redirect in PHP

To understand redirects in PHP you first need to remember how the web works. When a browser requests a page, the server answers with an HTTP message made of three parts: a status line with a code (for example 200 OK), a set of headers, and optionally the body with the HTML. PHP is the program that runs on the server and builds that response. A redirect is simply a special HTTP response: instead of returning the page content, the server sends a Location header with the URL the browser should go to, plus a status code that tells whether the move is permanent or temporary. The browser receives that response, reads the new URL and automatically issues a second request. To the user it looks instant: they type one address and land on another. That is the correct approach, and the one search engines use. Other techniques can take users to another page, such as the HTML meta refresh tag or JavaScript with window.location, but none of them replaces a real HTTP redirect when you need to move a URL, preserve rankings or protect a route: a PHP redirect happens on the server, before a single line of HTML is sent.

Why you need a redirect in PHP

There are many scenarios in which a page needs to answer with a PHP redirect. These are the most common:
  • The page changed its address: if you reorganize your site structure, migrate to another domain or remove a page, you redirect the old URL to the new one so nobody runs into a 404 error.
  • Friendly and canonical URLs: with redirects you unify the variants of the same page, for example from http to https, from www to non-www or from index.php to the root, so all traffic and SEO point to a single URL.
  • After submitting a form: when a form saves data, the correct response is a redirect to a confirmation page instead of plain HTML; that way, if the user refreshes, the browser does not resubmit the form or duplicate the order. This is the PRG pattern we will see later.
  • 301 redirects for SEO: a 301 tells Google that the page moved permanently and transfers most of the authority of the old URL to the new one.
  • Protecting access: if a page requires a logged-in user or a specific role, the script redirects visitors to the login page when the condition is not met.
In all these cases the redirect is not a decoration: it is part of the site architecture and affects user experience, security and search engine rankings.

How to make a redirect in PHP with header and Location

The key function is header(), which sends a raw HTTP header. To redirect, you send the Location header with the destination URL. The simplest example of a PHP redirect looks like this:
  • <?php
  • // Redirect the browser to another URL
  • header("Location: https://www.example.com/new-page");
  • exit;
That short. The header() function tells the browser: the page you asked for now lives at this other URL. When PHP finds a Location header without an explicit status code, it automatically adds a 302 Found, that is, a temporary redirect. We will talk about status codes in the next section. The exit after header is essential. The header is sent when the script finishes or when output starts flowing, but any code that comes after header() keeps running if you do not stop it. Without exit, the script could keep processing data, write to the database or send more headers that interfere with the redirect. The good practice is to write exit right after every header with Location. About the destination URL: the HTTP specification asks for an absolute URL, such as https://www.example.com/new-page, and it is the most compatible and recommended form. In practice, modern browsers also accept an absolute path of the same site, like header("Location: /new-page"), but if you want maximum compatibility, use the full URL. Another golden rule: the header() call must happen before the script produces any output, whether HTML, an echo or even a blank space. If the server has already started sending the body of the response, headers can no longer be modified and PHP raises the famous headers already sent warning. We will see how to avoid it below.

301 vs 302 redirects: permanent or temporary

Not all redirects mean the same thing. The HTTP status code that accompanies Location tells browsers and search engines whether the move is permanent or temporary, and that has practical consequences:
Code Name When to use it
301 Moved Permanently The page moved forever: search engines update the URL, SEO is preserved and users bookmarks are updated.
302 Found The move is temporary: maintenance, promotions or short-lived changes. Search engines keep the original URL.
303 See Other After processing a form sent with POST: the browser makes a GET request to the confirmation page (PRG pattern).
307 Temporary Redirect Like 302 but keeping the HTTP method: if the request was POST, the repeated request will also be POST.
The case that matters most for SEO is 301. When a page moves permanently, 301 Moved Permanently tells Google to index the new URL and to move the value of the old one there. That is why it is used when you change domains, reorganize the site architecture or consolidate duplicate content. 302, on the other hand, is for temporary changes, like a maintenance page or a short promotion: the search engine keeps the original URL in its results. To make a PHP redirect with status code 301, pass the code as the third argument of header():
  • header("Location: https://www.example.com/new-page", true, 301);
  • exit;
You can also set the code first with http_response_code() and then send the header:
  • http_response_code(301);
  • header("Location: https://www.example.com/new-page");
  • exit;
Both forms are valid; pick the one you find more readable. Use 301 with care, though: browsers and search engines cache permanent redirects, so if you try to undo one later, users may keep seeing the old version for a while. And avoid redirecting stray pages to the homepage just to avoid maintaining them: often a clean 404 is better than a confusing chain of redirects.

PRG pattern: redirecting after a form submission

One of the most valuable uses of the PHP redirect is the Post/Redirect/Get pattern, known as PRG. The problem it solves is classic: an order, registration or contact form sends its data with POST and the server processes it. If the script answers by printing the confirmation page, the browser keeps the POST request in its history; when the user presses F5 or goes back, the browser asks whether to resubmit the form and, if accepted, the order is processed twice. The PRG solution is that the script receiving the POST does not return HTML: after validating and saving the data, it answers with a redirect, normally 303 See Other, to a page reached with GET. That way refreshing only reloads the confirmation page, with no side effects:
  • <?php
  • // save_order.php: receives the form via POST
  • if ($_SERVER["REQUEST_METHOD"] === "POST") {
  • // validate and save the order here
  • save_order($_POST);
  • // 303 so refreshing does not repeat the submission
  • header("Location: /order-confirmed.php", true, 303);
  • exit;
  • }
303 See Other is the code designed for this case: it tells the browser to make a GET request to the given URL, even if the original request was a POST. That is the difference between a well-built PRG pattern and the annoying resubmission confirmation message of the browser.

Conditional redirects: protecting pages and choosing the destination

A PHP redirect is almost never a single line on its own: it usually depends on a condition. The typical case is protecting a private page. If the user is not logged in, they are redirected to the login page before seeing any content:
  • <?php
  • session_start();
  • // if there is no active session, send the visitor to login
  • if (!isset($_SESSION["user"])) {
  • header("Location: /login.php");
  • exit;
  • }
  • // the rest of the page only runs with an active session
The condition can be anything: that the user has a certain role, that the article exists in the database or that a URL parameter has a given value. When the destination depends on the request, redirect against a whitelist of allowed destinations instead of using the user value directly, to avoid open redirects, a vulnerability that lets attackers trick visitors into ending up on external sites:
  • <?php
  • // allowed destinations
  • $sections = array(
  • "profile" => "/profile.php",
  • "orders" => "/orders.php",
  • "help" => "/help.php"
  • );
  • $key = $_GET["go"] ?? "profile";
  • if (isset($sections[$key])) {
  • header("Location: " . $sections[$key]);
  • exit;
  • }
  • // if the key does not exist, go to the homepage
  • header("Location: /home");
  • exit;
Never build the Location URL by concatenating a raw value from $_GET, $_POST or $_SERVER without validating it: if the destination depends on user input, an attacker could use your page as a bridge to phishing sites.

Redirecting after a delay

Sometimes you do not want to jump immediately, but to show a message and take the user to the new page a few seconds later. The simple way is the Refresh header, which, although not part of the HTTP standard, is supported by all browsers:
  • header("Refresh: 5; url=https://www.example.com/new-page");
  • echo "We are redirecting you to the new page in 5 seconds...";
  • exit;
Another option for short delays is pausing the script with sleep() before sending the header: the browser waits and then jumps. Use it sparingly, because an unexplained wait hurts the experience. In practice, for confirmation or notice pages it is usually better to show the message with a link instead of forcing the user to wait, or to redirect immediately with a 303.

Common mistakes when making a PHP redirect

Even though the mechanics are simple, some mistakes repeat across projects. Knowing them will save you hours of debugging. 1. The headers already sent warning. It is the most searched error in PHP development and it appears when you call header() after the script has already sent output. Anything counts: an echo, a line of HTML, a space or line break outside the PHP tags, and even an invisible UTF-8 BOM that some editors add at the beginning of the file. Once the body of the response has started being sent, headers cannot be modified and the redirect does not happen:
  • <?php
  • echo "Welcome to the store";
  • // on a server without output buffering this already sends HTML
  • header("Location: /home");
  • // Warning: Cannot modify header information - headers already sent
To avoid it:
  • Place header() and its logic at the beginning of the script, before any output: structure the code to decide the redirect before rendering HTML.
  • Omit the closing ?> tag at the end of files that only contain PHP, so no trailing spaces or line breaks are sent as output.
  • If you cannot avoid earlier output, enable the output buffer with ob_start() at the start of the script; output is held back and header() can run afterwards.
  • Check that the file was not saved with a UTF-8 BOM, because those three invisible bytes at the beginning also count as output.
  • Review that there are no spaces or blank lines before the opening <?php tag.
The output buffer solves the problem when you cannot reorder the code:
  • <?php
  • ob_start(); // from here on, output stays in the buffer
  • // ... any echo or HTML is held back ...
  • header("Location: /home"); // no conflict anymore
  • exit;
2. Forgetting the exit after header(). If the script keeps running, the rest of the code executes: it may send more headers, display content or process data that should not be processed. Get used to writing exit right after every redirect. 3. Creating redirect loops. If the destination URL redirects back to the original, the browser enters a cycle and ends up showing a too many redirects error. This happens when debugging protected routes: check that the login page does not redirect to the page that redirects to login. A good way to see the whole chain is to test with curl from the terminal, because curl -I shows only the response headers:
  • curl -I https://www.example.com/old-page
  • HTTP/1.1 301 Moved Permanently
  • Location: https://www.example.com/new-page
4. Using 301 for changes that are not permanent. Because 301 redirects are cached by browsers and search engines, a permanent redirect set by mistake is hard to undo. Reserve 301 for definitive moves and use 302 or 303 for everything else.

In summary

Making a redirect in PHP is simple, but doing it well requires understanding HTTP. The rule that summarizes the whole article is: header("Location: URL") before any output, with the right status code and followed by exit.
  • header() with Location sends the browser to another URL and must be called before any output.
  • Without an explicit code, PHP answers with a temporary 302; use the third argument of header() or http_response_code() to send 301, 303 and the rest.
  • The PRG pattern answers a POST with a 303 redirect to avoid duplicates when refreshing.
  • Use conditional redirects to protect pages and always validate the destination against a whitelist.
  • If you see headers already sent, review previous output, remove the closing PHP tag and try ob_start().
  • Check your redirects with curl -I to see the real status code and destination URL.
With these foundations you can implement clean, safe and SEO-friendly redirects in any PHP project, from a small script to a complete application.
Chatea por WhatsApp