Developers

Redirect

A task that changes something should not render a page. It should redirect, so that reloading the browser does not repeat the change. App::redirect() is how, and it is the last line of nearly every save, delete, publish and state task in the tree.

use App;
use Route;
use Lang;

App::redirect(
    Route::url('index.php?option=com_bookings&controller=instruments'),
    Lang::txt('COM_BOOKINGS_BOOKING_SAVED'),
    'success'
);

The member lands on the list, sees the message, and the URL in their address bar is a GET they can reload, bookmark or share. Render the list directly instead and the URL is still the POST that saved the booking, so a refresh offers to save it again — and a member who says yes gets two.

The arguments

App::redirect($url, $message = null, $type = 'success')

Argument Notes
$url Where to go. Build it with Route::url() rather than by hand
$message Optional. Shown to the member on the page they land on
$type success, error, warning or info

The message is queued through the notification service, which stores it in the session; the template on the next page renders and clears it. That is why the message survives the redirect. On a client with no notification service — the API, the command line — the message is dropped and only the redirect happens.

The types match the Notify facade's methods, so these two are equivalent:

App::redirect($url, $msg, 'error');
Notify::error($msg);

App::redirect($url);

Use the second form when you have several messages to queue, or when the redirect target is decided later.

It does not return

App::redirect() builds a Hubzero\Http\RedirectResponse, sends it, and calls App::close(), which is exit(). Nothing after the call runs:

App::redirect(Route::url('index.php?option=com_bookings'));

// never reached
$this->doSomethingElse();

That makes it safe to use as an early exit, and it makes return after it redundant — though return is written in a lot of places anyway, and does no harm.

What happens to the URL

RedirectResponse::send() normalises the target before sending it:

  • A URL starting index.php or index2.php gets Request::base() prepended.
  • Everything after the first line break is discarded, which is what stops a header injection through a redirect parameter.
  • A URL with no scheme is made absolute: one starting / against the current scheme, user info and host; anything else against the current path as well.

So a relative route result reaches the browser as an absolute URL, and you do not have to build one.

Redirecting from a controller

Controllers extending Hubzero\Component\SiteController or AdminController also carry setRedirect($url, $msg = null, $type = null), which stores the target, and redirect($url = null, $msg = null, $type = null), which sends whatever was stored. Both are marked @deprecated and both end up in App::redirect(); you will meet them in older controllers, but write App::redirect() in new code.

Sending one yourself

The rare case — a middleware, or a redirect that must carry extra headers — constructs the response directly:

$redirect = new Hubzero\Http\RedirectResponse($url);
$redirect->setRequest(App::get('request'));
$redirect->header('X-Reason', 'moved');
$redirect->send();

App::close();

setRequest() is what enables the URL normalisation above; without it the target is sent exactly as given.

Rewritten and checked against 2.4-main @ 348f0057c2 on 2026-09-10.