Contents

Redirect code generator

Type the old address and the new one, choose the status, and decide what happens to the visitor's query string and a trailing slash. Copy the version for your server.

This tool needs JavaScript. Everything it does runs in your browser — nothing you type is sent anywhere.

Every snippet here was run on a real Apache, nginx, PHP and Symfony server before this page was published. Nothing you type leaves your browser.

Which status to use

Status Meaning Use it when
301 Moved permanently A page has a new address for good. Search engines move the old address's standing to the new one.
302 Found, temporarily The old address will come back: a sale page, a maintenance notice.
307 Temporary, method kept As 302, for an address that receives form posts or API calls.
308 Permanent, method kept As 301, for an address that receives form posts or API calls.

A browser following a 301 or 302 may turn a POST into a GET, so a form submitted to the old address arrives at the new one empty. 307 and 308 forbid that. For an ordinary page nobody posts to, 301 is the answer.

The two choices this generator will not guess

The visitor's query string. A link to /old-page/?ref=newsletter can arrive at the new page with ?ref=newsletter or without it. Passing it on keeps campaign tracking working; dropping it is right when the old parameters mean nothing to the new page. Apache and nginx can do either. Symfony's built-in redirect cannot choose: it always passes the query string on to a path on the same site and never to a full https:// address, and the generator says so rather than writing a route that does the opposite of what you asked.

The trailing slash. /old-page and /old-page/ are different addresses to a server. "Match with or without" redirects both, which is almost always what you want after a migration, because old links exist in both forms.

How to redirect a page in PHP

Send the Location header before anything else is output, then stop the script:

<?php
header('Location: /new-page/', true, 301);
exit;

The third argument of header() is the status; leave it out and PHP sends a 302, which tells search engines the move is temporary. The exit matters: without it the rest of the old page still runs, and anything it does, such as writing to a database, still happens. If PHP says headers were already sent, something was output first: often a blank line or a byte-order mark before the opening <?php.

Where each snippet goes

  • .htaccess: the file in the folder the old address lives under, usually the site root, above any WordPress or framework block that would otherwise catch the request first.
  • nginx: inside the server { } block for the site, then nginx -t to check it and nginx -s reload to apply it.
  • PHP: at the very top of the script the old address runs, or of the front controller if every request goes through one.
  • Symfony: in config/routes.yaml. php bin/console debug:router lists it once it is loaded.