How do you set a cookie from within an ESI:include script? - php

I have a basic PHP page being loaded through Varnish with a single ESI include that calls back to the server to set a cookie. The cookie is set with domain access and the like, but when called through ESI the cookie is never set. If you access the ESI include path directly, the cookie is set with no issue. I have even set my Varnish configuration to never cache anything, thinking the VCL could be killing the cookie.
This...
<esi:include src="/init.php?<?=http_build_query($_GET); ?>"></esi:include>
...includes this...
<?php
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
setcookie('superman', 'clark kent', 0, '/', '.whatever.com');
?>
I'm new with Varnish and ESI, so I'm starting to wonder if this is a known limitation (either with ESI or with Varnish's ESI implementation), but I can't find any discussion of my problem online.

An interesting question which has been asked -but not answered- before ( Setting Cookies via ESI:include, how? ). I don't think you can do it this way. With ESI-include, the ESI processor makes a separate request and replaces a part from the body, not the header. In order to make your preferred set-cookie behavior work correctly, the ESI specification should specify how to 'merge' all set-cookie headers.
See chapter six of the ESI spec: http://www.w3.org/TR/esi-lang
When an ESI template is processed, a
separate request will need to be made
for each include encountered.
Implementations may use the original
request's headers (e.g., Cookie,
User-Agent, etc.) when doing so.
Additionally, response headers from
fragments (e.g., Set-Cookie, Server,
Cache-Control, Last-Modified) may be
ignored, and should not influence the
assembled page.
Could you try to convert your set-cookie header in a javascript set-cookie script? This could be included in the body...

Related

How to set cookie in Laravel Blade File?

I want to set cookies in laravel blade.php file, not in the controller. How can I set it?
Disclaimer: I will focus my answer on PHP and laravel.
Why not set in controller?
It would really help to know why you cannot / or do not want to set cookies using laravel's cookie Facade in the controller - eg. Cookie::queue, as it's very easy to do!
Here are two ways, from this source.
Via response:
return response(view('welcome'))->cookie('name','value',$min);
Via Queue: Cookie::queue(Cookie::make('name','value',$min)); return view('welcome');
Set-Cookie is a response header, not the body!
Assuming you would set these cookies in PHP , they need to come as part of a response header, and not part of the body (view). This is why you would need to set these in the controller, where you are sending a response!
If you try to use PHP functions to set cookies, you will be met with errors "headers have already been sent"
Per the docs: https://www.php.net/setcookie
setcookie() defines a cookie to be sent along with the rest of the HTTP headers. Like other headers, cookies must be sent before any output from your script (this is a protocol restriction). This requires that you place calls to this function prior to any output, including and tags as well as any whitespace.
To understand what this means, it's helpful to understand the structure of requests and responses:
Requests and Responses are made up of headers and possibly a body.
Note: You can see these in the network tab of your browser's dev tools.
The request headers are like meta data about the request that can tell the server what kind of content is being requested, and who is requesting.
The response headers are like meta data about the response returned that can tell the server what kind of content is being delivered, how long to cache it for, associated cookies that got set.
Example Request Headers:
Content-Type: 'application/json'
Content-Type: 'application/pdf'
Content-Type: 'text/html'
Content-Type: 'text/css'
User-Agent: 'Mozilla/5.0 (<system-information>) <platform> (<platform-details>) <extensions>'
Authorization: 'Bearer <token>'
Example Response Headers:
Content-Type as it may differ from what was requested
Expires: 'Wed, 07 Sep 2022 19:26:49 GMT'
Cross-Origin-Resource-Policy: 'cross-origin'
Date: 'Wed, 07 Sep 2022 19:26:49 GMT'
Content-Length: 0,
Set-Cookie: test_cookie=CheckForPermission; expires=Wed, 07-Sep-2022 19:41:49 GMT; path=/; domain=.doubleclick.net; Secure; HttpOnly; SameSite=none
Notably: - Set-Cookie - tells the browser to add these cookies to application storage (you can view these in application / storage tabs in dev tools)
The response header can have Set-Cookie, not the request header. This makes sense, as usually the cookie information is going to come from the "answer" (response) to the "question" (request) by way of performing some logic, eg - this user is authenticated, here's a cookie to keep their session in place.
Also: Secure & HTTP only Cookies
Cookies can get set with a few options - secure only, and http only. These mean that the cookie must be Set on secure connections (https) and the http only can come from a response and cannot be overridden by JavaScript adjusting (client side)
Example of options for Laravel's Cookie::queue facade:
// $name, $value, $minutes = 0, $path = null, $domain = null, $secure = false, $httpOnly = true
Cookie::queue($name, $value, $ttl, $path, $domain, $secure, $httpOnly);
ttl = "time to live" or how long until it expires eg. 2 minutes

Laravel redirect with no cache header

I'm using Laravel 3 and it's not obvious how to set headers in any way other than through Response::make().
I am doing a redirect like this:
return Redirect::to('admin/check');
I'd like to set an additional no-cache header for the redirect like so:
"Cache-Control: no-store, no-cache, must-revalidate"
I realize I could just do this directly in PHP, but is there any way to set response headers via Laravel?
When you call Redirect::to() Laravel instantiates a Response object with 302 status and a Location header. That Response object is then returned by the controller and rendered as a proper HTTP response, so, at controller time, you can still change its headers.
To be even more precise class Redirect extends Response. Take a look here
You can achieve that by simply using:
return Redirect::to('admin/check')
->header('Cache-Control', 'no-store, no-cache, must-revalidate');
I'm afraid, the accepted answer is wrong and misleading!
It's impossible to redirect to a page with custom headers set, no matter what language or framework you use. In other words, there's no way to trigger an HTTP redirect and cause the client (browser) to add a custom header.
You might be thinking that this code should work just fine:
return Redirect::to('admin/check')
->header('Cache-Control', 'no-store, no-cache, must-revalidate');
But it won't. You're setting the custom headers for the response which is instructing the browser to redirect, not for the redirect itself.
The only way for a site to instruct a browser to issue an HTTP request with a custom header is to use Javascript and the XMLHttpRequest object. And it needs CORS implemented on the target server to allow such ajax requests.
Please note that a page can not set HTTP request headers unless it's making an async request using XMLHttpRequest. Meaning that you can't do such redirection with the custom header on the client-side as well.
That's not how the web works.

PHP cookie handling

A centain web client that I need to support, is sending back the Cookies header to my application twice in the HTTP headers, this in turn is making PHP unable to read the correct value for the cookie thus ignoring the session.
Here is the relevant part of the request I am seeing:
GET / HTTP/1.1
Cache-Control: max-age=0
Accept-Language: en-US
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a
Connection: Keep-Alive
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a
[...] Other irrelevant headers
I have two questions:
Is that a PHP bug? or is the behavior undefined when the client sends that same header twice?
Is there a quick workaround to make things work without having to manually parse the HTTP headers so I can read the right value of the cookie (and session) in my application? Or should I manually parse the HTTP header to set the session to its correct value?
According to the HTTP spec, a double header simply concatenates the values together with a comma, making it:
Cookie: PHPSESSID=49af82ddf12740e6a35b15985e93d91a, PHPSESSID=49af82ddf12740e6a35b15985e93d91a
PHP should be able to parse the cookies, but the behavior of sessions is undefined when there are two session IDs.
I strongly recommend fixing the client. If that's not an option, you'll have to parse the headers manually.

Send HTTP headers before or after a cookie header?

I was wondering if there are any problems or difference between sending normal headers before or after sending cookie headers. Do some browsers prefer a certain order to headers? If the cookie header is to large would subsequent headers never be parsed?
setcookie("TestCookie", $value);
header("Content-type: text/javascript");
or
header('Location: http://www.example.com/');
setcookie("TestCookie", $value);
or
setcookie("SuperLargeCookie", $massive_value);
setcookie("TinyCookie", $small_value);
header("Status: 404 Not Found");
There is no difference. The Http protocol does not specify that headers are to be in a certain order. Browsers do not differentiate based on the order of headers either.
The total length of Http headers does have a limit. This limit is imposed by the server and not the browser. Typically between 8K and 16K. However this is configurable.
It really doesn't matter as long as the other HTTP headers have not been sent. setcookie() actually writes a header itself:
Set-Cookie: SuperLargeCookie=whatever; Max-Age=3600; Version=1
similar to a header() call:
Location: http://www.example.com/redirect
HTTP messages span packets all the time, so you'd be hard-pressed to overfill one unless you're jamming tons of kilobytes in there. If you need to do that, consider a better design. Browsers don't care about the order of headers since different servers (and applications) append headers all the time. Cookies are implemented as HTTP headers, so they should appear like so in the HTTP request:
Cookie: TestCookie=value\r\n
Content-type: text/javascript\r\n
\r\n
I'm not sure what the Status header is supposed to do in your example, but I don't think it's right since the webserver will set a 200 OK response code if the code executes correctly... The header function page has this examaple:
<?php
header("HTTP/1.0 404 Not Found");
?>
With the PHP header function, just make sure you're not writing any text out before issuing it. Otherwise, you could mess everything up.

Delete a header in PHP

To allow caching a PHP generated file, I want to make sure, that the 'Pragma: no-cache' header is not set. However, how do I delete a possibly already set header?
That is, it could be possible, that somewhere in the code someone wrote header('Pragma: no-cache'); and now I want to make sure, the header is not sent.
Is it sufficient to do this:
header('Pragma:');
or is there something like delete_header() (which would, apparently, be undocumented or well-hidden)?
header_remove() since php 5.3
header_register_callback() is also coming soon
I know this question is old and already answered. But some of the answers could leave folks with the wrong impression. Rest assured that if your response headers contain Pragma: no-cache it absolutely will in fact prevent a web browser from caching a resource regardless of other settings.
So of course if you are using at least PHP 5.3, you can remove the Pragma header using header_remove( 'Pragma' );.
You can override a previously set header by passing a second argument to header():
void header ( string $string [, bool $replace = true [, int $http_response_code ]] )
Check the manual for header()
The 'pragma' headers behaviours are not defined by the spec - despite the widely held believe that sending a 'Pragma: No-cache' header will have some effect on the browser, in fact it is almost universally ignored (and is never returned by any php installation I've used).
To tell the browser NOT to cache content is done via an expires header with a date in the past, a Cache-Control header with a no-cache value, or (if you want to be sneaky) by a 'Varies: Date' header. In the absence of any of these types of header the client must not cache the page.
So, conversely, if you want a page to be cacheable, set the expires and cache-cntrol headers.
C.

Categories