skip to content

In Laravel, how do you set a cookie with response()->cookie() or withCookie() versus Cookie::queue(), and when do you need each?

level: middleimportance: should knowfreq 42%

answer

  1. have the response yet, or not
  2. cookie($name, $value, $minutes)
  3. 0 minutes means a browser-session cookie
  4. queued cookies added by AddQueuedCookiesToResponse
  5. withoutCookie() and Cookie::expire() delete

basics

~20 s

Chain ->cookie() or ->withCookie() when you hold the response object. Use Cookie::queue() when the code setting the cookie runs before the response exists; the web group's AddQueuedCookiesToResponse middleware adds queued cookies to whatever response comes back.

solid answer

~40 s

`$response->cookie('seat_hold', $id, 10)` is an alias of `withCookie()`: given a name it calls the `cookie()` helper to build a Symfony `Cookie` (value, lifetime in **minutes**, then path, domain, secure, httpOnly, raw, sameSite) and sets it on the response headers. That needs the response in hand. When a service or listener decides on a cookie before the controller has built anything, `Cookie::queue('seat_hold', $id, 10)` stores it in the cookie jar, and the `AddQueuedCookiesToResponse` middleware in the `web` group copies every queued cookie onto the outgoing response. A lifetime of `0` means a session cookie that dies with the browser; `Cookie::forever()` means 400 days. Path, domain, secure and same-site default to the values in `config/session.php`. To delete, call `withoutCookie('seat_hold')` or `Cookie::expire('seat_hold')`.

code

php · 12 lines
php
<?php

use Illuminate\Support\Facades\Cookie;

// Deep in a service: no response exists yet.
Cookie::queue('seat_hold', $hold->id, 10);        // 10 minutes

// In a controller: chain onto the response.
return response()
    ->view('venues.chosen', ['venue' => $venue])
    ->cookie('venue_pref', $venue->slug, 60 * 24 * 30)
    ->withoutCookie('seat_hold');

go deeper

for a junior

Know the two styles: chain cookie() or withCookie() on a response, or call Cookie::queue() when there is no response yet. The lifetime is in minutes.

for a middle

Explain that queued cookies are copied onto the response by AddQueuedCookiesToResponse in the web group, that 0 minutes means a session cookie, and where default attributes come from.

for a senior

Keep one mechanism per cookie, delete with withoutCookie() or Cookie::expire(), and check which middleware group a route runs in before relying on the queue.

for a principal

Set team conventions for which state lives in cookies versus the session, and keep cookie defaults centralized in session config rather than repeated per call.

## Two ways to get a cookie onto a response A cookie reaches the browser as a `Set-Cookie` header on a response. Laravel lets you put it there in two ways, and the difference is simply **whether you have the response object yet**. In a concert-venue app, a buyer who picks a seat gets a short-lived `seat_hold` cookie, and the app remembers the preferred venue in a long-lived `venue_pref` cookie. ## Attaching directly to a response Every `Illuminate\Http\Response`, `JsonResponse` and `RedirectResponse` has these methods from `ResponseTrait`: - `cookie($cookie, ...)` is an alias that forwards all its arguments to `withCookie()`. - `withCookie($cookie, ...)` accepts a ready `Symfony\Component\HttpFoundation\Cookie`, or a name followed by the `cookie()` helper's arguments, and calls `$this->headers->setCookie()`. - `withCookies([...])` sets several `Cookie` instances at once. - `withoutCookie($name, $path, $domain)` sends the cookie again with an expiry far in the past, which deletes it in the browser. The `cookie()` helper, and `Cookie::make()`, take `($name, $value, $minutes = 0, $path = null, $domain = null, $secure = null, $httpOnly = true, $raw = false, $sameSite = null)`. Two defaults matter: 1. **Minutes, not seconds.** `cookie('seat_hold', $id, 10)` lasts ten minutes. A lifetime of `0` produces a session cookie with no expiry date, which the browser drops when it closes. `Cookie::forever()` uses 576000 minutes, which is 400 days. 2. **Unset attributes come from session config.** The cookie jar is created with the `path`, `domain`, `secure` and `same_site` values from `config/session.php`, so a bare `cookie('venue_pref', 'north-hall', 60)` inherits them. `httpOnly` defaults to `true`, so JavaScript cannot read it. ## Queueing when no response exists yet Often the code that decides on a cookie is not the code that builds the response: a seat-hold service called from a controller, an event listener, or a helper deep in the stack. For those, the `Cookie` facade offers a queue: - `Cookie::queue('seat_hold', $id, 10)` builds the cookie with the same arguments as `make()` and stores it, keyed by name and path, in the singleton `CookieJar`. - `Cookie::queue($cookieInstance)` queues a cookie you already built. - `Cookie::expire('seat_hold')` queues a deletion. - `Cookie::unqueue('seat_hold')` cancels a queued cookie. Nothing is sent at that moment. The `Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse` middleware runs `$next($request)`, takes the finished response and calls `$response->headers->setCookie()` for every queued cookie. That middleware sits in the default `web` group, just after `EncryptCookies`. ## Choosing between them | Situation | Use | |---|---| | The controller returns a response and knows the cookie | `->cookie()` or `->withCookie()` | | A service or listener sets it before any response exists | `Cookie::queue()` | | Delete while building the response | `->withoutCookie('name')` | | Delete from deep inside the request | `Cookie::expire('name')` | | Long-lived preference | `Cookie::forever()` or a large minutes value | ## Reading them back On the next request, `$request->cookie('venue_pref')` returns the value. In the `web` group the `EncryptCookies` middleware encrypts outgoing cookie values and decrypts incoming ones, so the browser only ever sees ciphertext; that middleware and its exceptions are part of the default stack, and the meaning of `Secure`, `HttpOnly` and `SameSite` belongs to HTTP cookies in general. ## Common mistakes - **Seconds instead of minutes.** `cookie('seat_hold', $id, 600)` keeps a hold for ten hours, not ten minutes. - **Chaining onto a throwaway response.** Calling `response()->cookie(...)` in a service creates a new response that nobody returns; queue the cookie instead. - **Deleting with an empty value.** Setting the value to `''` still leaves a cookie behind; `withoutCookie()` or `Cookie::expire()` sends an expiry in the past so the browser removes it. - **Deleting with the wrong path or domain.** A cookie set with a custom path must be expired with the same path, or the browser keeps the original. - **Assuming the queue works everywhere.** It depends on the route running the `web` middleware group. ## What interviewers probe They want the "do you have the response yet" distinction, the minutes unit and zero meaning session cookie, and an awareness that queued cookies depend on a middleware in the `web` group. The best answers also know how to delete a cookie in both styles.

  • Why does calling `Cookie::queue('seat_hold', ...)` twice in one request send only one seat_hold cookie?
    `CookieJar::queue()` stores cookies in an array keyed by name and then by path, so a second call with the same name and path overwrites the first. Only the last value reaches `AddQueuedCookiesToResponse`. Use `Cookie::unqueue()` to cancel a queued cookie before queueing a replacement if the intent should be explicit.
  • Why does `cookie('venue_pref', 'north-hall')` disappear when the user restarts the browser?
    The third argument, minutes, defaults to 0, and the cookie jar turns 0 into an expiry of 0: a session cookie with no Expires attribute. Browsers discard those when the session ends. Pass a lifetime in minutes, or use `Cookie::forever()`, for a persistent preference.

saying these in an interview costs you the question

  • The cookie lifetime argument is in seconds.
  • A lifetime of 0 makes the cookie permanent.
  • Cookie::queue() sends the Set-Cookie header immediately.
  • You must pass path, domain and secure every time you create a cookie.
  • A cookie can only be deleted by setting its value to an empty string.