Building Responses

Building Responses and Attaching Headers

When status, headers or cookies matter, build the response: response($body, $status) or response()->view(), whose methods chain. The category page's show action:

A view response with headers and a cookiePHP
return response()
    ->view('categories.show', ['category' => $category->load('products')])
    ->header('X-Product-Count', (string) $category->products->count())
    ->withHeaders(['Content-Language' => 'en', 'X-Frame-Options' => 'DENY'])
    ->cookie('last_category', $category->slug, 60 * 24 * 30)      // minutes
    ->withoutHeader('X-Powered-By');
Output
$ curl -s -D - -o /dev/null $B/categories/computing \
    | grep -E '^(X-|Content-Lang|Set-Cookie: last)' \
    | sed -E 's/=eyJ[^;]*/=eyJ.../; s/ exp[^;]*;//'
X-Powered-By: PHP/8.5.4
X-Product-Count: 2
Content-Language: en
X-Frame-Options: DENY
Set-Cookie: last_category=eyJ...; Max-Age=2592000; path=/; httponly; samesite=lax
$ E=$(curl -s -D - -o /dev/null $B/categories | grep ETag | cut -d' ' -f2 | tr -d '\r')
$ curl -si -H "If-None-Match: $E" $B/categories | head -1
HTTP/1.1 304 Not Modified

EncryptCookies encrypted the cookie. withoutHeader('X-Powered-By') did nothing, because PHP adds that header below Laravel 2,157 : set expose_php = Off (php.ini and Extensions) instead. The cache.headers middleware on the index route (Basic Controllers) sent Cache-Control: max-age=300, public and an ETag hashed from the body, then answered 304 when it matched; the action still ran. Never mark personal pages public.