skip to content

Redirects & Intended URLs

redirect(), to_route() and back() build redirect responses, and intended() returns a user to the page they were bounced from. Interviewers ask about flashing input and errors and 302 vs 303.

on this pageshow

explore

questions

5

In Laravel, how do redirect()->to(), redirect()->route(), to_route(), back() and redirect()->away() differ, and which status do they send?

level: juniorimportance: must knowfreq 60%

answer

  1. path, route name, previous page, outside URL
  2. all build an Illuminate\Http\RedirectResponse
  3. default status 302 on every builder
  4. to_route() is redirect()->route()
  5. back() reads Referer, then the session

basics

~20 s

All five return an Illuminate\Http\RedirectResponse with status 302 unless you pass another code. to() takes a path or URL, route() and to_route() a route name, back() the previous page, and away() an external URL used as-is.

solid answer

~40 s

`redirect()` with no arguments returns the `Redirector`; with a path it is shorthand for `redirect()->to($path)`. `to('/basket')` turns a path into an absolute URL of your app. `route('orders.show', $order)` generates the URL from a route name and parameters and throws `RouteNotFoundException` for an unknown name; `to_route()` is the global helper for exactly that call. `action([OrderController::class, 'show'], $order)` builds the URL from a controller action. `back()` targets the `Referer` header, then the previous URL stored in the session, then the fallback you pass, or `/`. `away('https://pay.example')` skips URL generation entirely. Every builder takes the status as an argument and defaults to **302**, so a 303 or 301 must be asked for.

code

php · 11 lines
php
<?php

use App\Http\Controllers\OrderController;
use App\Models\Book;

return redirect('/basket');                                  // path -> 302
return to_route('books.show', $book);                        // named route -> 302
return redirect()->action([OrderController::class, 'index']); // controller action
return back(fallback: route('books.index'));                 // previous page
return redirect()->away('https://pay.example/session/42');   // external, as-is
return to_route('orders.show', $order, 303);                 // explicit 303

go deeper

for a junior

Know the four targets: a path with to(), a route name with route() or to_route(), the previous page with back(), and an external URL with away(). All default to 302.

for a middle

Explain how back() picks its target, why named routes survive URL changes, and how to pass a different status such as 303 or 301 to any builder.

for a senior

Standardise on named-route redirects, give back() a fallback, and treat to() and back() with client-controlled values as potential open redirects.

for a principal

Set conventions for redirect statuses and targets across teams so POST handlers, permanent moves and external hand-offs behave the same everywhere.

## One response class, several ways to aim it In Laravel a redirect is an `Illuminate\Http\RedirectResponse`: a response with a 3xx status and a `Location` header. You rarely construct it yourself. The `redirect()` helper returns the `Illuminate\Routing\Redirector` when called without arguments, and its methods differ only in **how they compute the target URL**. All of them attach the current session and request to the response, so you can chain `with()`, `withInput()` or `withErrors()` afterwards. In an online bookshop the same controller might send users to a path, a named route, the previous page or an external payment page. ## The builders side by side | Call | Target is computed from | Default status | |---|---|---| | `redirect('/basket')` or `redirect()->to('/basket')` | a path, made absolute with the app's URL; full URLs pass through unchanged | 302 | | `redirect()->route('books.show', $book)` | a route name plus parameters | 302 | | `to_route('books.show', $book)` | the same as `redirect()->route()` | 302 | | `redirect()->action([BookController::class, 'show'], $book)` | a controller action | 302 | | `back()` or `redirect()->back()` | `Referer`, else the session's previous URL, else the fallback | 302 | | `redirect()->away('https://pay.example/session/42')` | the string you pass, untouched | 302 | A few details in each: - **`to()`** hands the path to the URL generator's `to()` method. A relative path becomes `https://shop.test/basket`; anything the generator already considers a valid URL, including `https://...` and protocol-relative `//host/...` strings, is returned as-is. - **`route()`** throws `Symfony\Component\Routing\Exception\RouteNotFoundException` ("Route [x] not defined.") when the name is unknown, which is what you want during development. Parameters can be an Eloquent model, whose route key fills the placeholder. - **`to_route($route, $parameters = [], $status = 302, $headers = [])`** is defined in `Illuminate\Foundation\helpers.php` as `return redirect()->route(...)`. It is purely shorter. - **`action()`** throws `InvalidArgumentException` when no route points at that action. - **`back($status = 302, $headers = [], $fallback = false)`** asks the URL generator for `previous()`. That reads the `Referer` request header first; without one it uses the URL that `StartSession` stored for the last GET page view; without that it uses `$fallback`, and finally `/`. - **`away()`** calls the `RedirectResponse` constructor directly, with no URL generation, encoding or validation. The `Redirect` facade exposes the same methods: `Redirect::route('basket.show')`. ## Status codes Every builder has a `$status` parameter that defaults to **302 Found**. Pass another code positionally: 1. `redirect('/basket', 303)` or `to_route('orders.show', $order, 303)` for a POST-redirect-GET that must become a GET. 2. `redirect()->to('/new-path', 301)` for a permanent move you want browsers and crawlers to remember. 3. `back(303)` for the "go back after a form" case. For permanent redirects between URLs of your own site, a route-level redirect definition is often cleaner than a controller, but that belongs to routing. ## Picking the right one - Prefer **`to_route()` or `route()`** for pages of your own app: renaming a URL does not break the redirect. - Use **`back()`** after a form that can be posted from several pages, such as "add to basket" on both the listing and the book page, and give it a fallback. - Use **`away()`** only for external destinations you chose yourself, such as a payment provider. - Use **`to()`** for literal paths, and never with an unchecked user-supplied value. ## Common mistakes - **Forgetting `return`.** Calling `redirect()->route('basket.show');` without returning it builds a response that is thrown away, and the controller carries on to whatever comes next. - **Passing the status in the parameters slot.** `to_route('basket.show', 303)` treats 303 as a route parameter; the status is the third argument: `to_route('basket.show', [], 303)`. - **Hard-coding paths.** `redirect('/books/'.$book->id)` breaks the day the URI changes, while `to_route('books.show', $book)` keeps working. - **Using `back()` without a fallback** on pages people reach from bookmarks or emails, where there is no `Referer` and the session may be new, so the user ends up on `/`. - **Using `away()` for internal pages.** It skips URL generation, so an internal path passed to it is not made absolute or checked against your routes. ## What interviewers listen for They expect the four targets (path, name, previous page, external), the 302 default and how to change it, and the fact that `to_route()` is only a shortcut. Mentioning that `back()` relies on the `Referer` header, which the client controls, shows you know where the URL comes from.

  • What does `back()` do when the request has no Referer header?
    It falls back to the previous URL that the `StartSession` middleware stored for the last GET page view in the session. If there is none either, it uses the `$fallback` argument, and if that is also missing it redirects to `/`. Passing a fallback such as `back(fallback: route('books.index'))` makes the result predictable.
  • Why prefer `to_route('basket.show')` over `redirect('/basket')`?
    The route name is resolved by the URL generator at runtime, so changing the URI in the route file does not break any redirect that points at it. An unknown name also fails loudly with `RouteNotFoundException` instead of sending users to a 404 page.

saying these in an interview costs you the question

  • redirect()->route() sends 301 because named routes are permanent.
  • to_route() behaves differently from redirect()->route() and skips the session.
  • back() always reads the previous URL from the session, never from a header.
  • away() validates that the URL belongs to an allowed host.
  • Laravel redirects use 303 by default after a POST request.
open as a page

In Laravel, how does redirect()->intended() send a bookshop customer back to the checkout page after login, and where does url.intended come from?

level: middleimportance: must knowfreq 55%

basics

~20 s

When a guest hits a protected page, the exception handler calls redirect()->guest() to the login route, which stores the current URL as url.intended in the session. After login, redirect()->intended($default) pulls that key and redirects there, or to $default.

open as a page

In Laravel, why do redirects after a POST default to 302, when would you send 303 instead, and how do you do it?

level: middleimportance: should knowfreq 32%

basics

~20 s

Laravel's redirect builders default to 302. Browsers turn a 302 after a form POST into a GET, so it works for HTML forms, but only 303 guarantees a GET for any method; pass it as the status argument.

open as a page

On a Laravel RedirectResponse, what do with(), withInput() and withErrors() each put in the session for the next request?

level: middleimportance: should knowfreq 50%

basics

~10 s

with() flashes any key and value, withInput() flashes the request input under _old_input without uploaded files, and withErrors() flashes a MessageBag into the errors ViewErrorBag. All three survive only the next request.

open as a page

A Laravel bookshop redirects after login with redirect()->to($request->input('return')); why is that an open redirect, and how do you fix it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Redirector::to() returns any string its URL generator considers a valid URL unchanged, including https:// and protocol-relative //host values, so a crafted return parameter sends users to another site. Redirect only to named routes, intended(), or a same-origin check.

open as a page