-
Notifications
You must be signed in to change notification settings - Fork 11.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[5.6] Add a HTTP cache middleware #22389
Conversation
better as a package? |
@browner12, it looks like a basic web framework feature to me (Symfony has something similar, Django too) but as I'm new to Laravel contribution, I don't know where it should belong. |
public function handle($request, Closure $next, int $maxAge = null, int $sharedMaxAge = null, bool $public = null, bool $etag = false) | ||
{ | ||
/** | ||
* @var \Symfony\Component\HttpFoundation\Response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing $response
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has been removed by StyleCI actually (29a3e96#diff-b3ff80d2f50088f0bfe3b4e15d654af9L23). It looks like a bug.
my experience with HTTP caching headers has been on the web server. not sure if it's a good idea or not to have it at the app level. the only reason I suggested it as a package, is historically Taylor kept these types of things out of core and suggests it that way, but I'm not necessarily opposed to it in core. |
} | ||
|
||
if (! $response->getContent()) { | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks weird. Shouldn't we always return something from middlewares?
return; | ||
} | ||
if ($etag) { | ||
$response->setEtag(md5($response->getContent())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do I set a custom etag?
This looks incomplete. We would still need a middleware that checks the
|
Indeed it doesn't support 100% of the spec. It is a deliberate choice because it's not possible to use named arguments for a middleware, and middlewares with a lot of arguments are not really practical. An alternative would be to use the Route::get('/my-route', function () {
return view('welcome');
})->middleware('cache:max_age=180;s_maxage=60;etag=abc;immutable=true'); Do you think such syntax is acceptable? About your specific questions:
It's the default value hardcoded in the Symfony Response.
It is not supported (see initial comment) but if we want to support them, I can add new flags for no-cache and those ones.
I've explicitly not supported them because
Yes, for now it's delegated to the reverse proxy but I plan to open another PR with such middleware when this one will be ready. Support for |
I believe that this part is mega-super-important. There's deliberate choices taken, but those were initially totally undocumented. There were nothing in the PR description that says "I decided to do this partially, some things are done, other parts have to be added laters". So this PR is admittedly incomplete since it lacks a middleware to handle the requests, and you are expected to run a reverse proxy in front of nginx to have this work. There's nothing in this PR on how to setup that proxy, and we would need an entire new section in the docs for this. (Or handle the wrath of the support questions about how to do it; "Hey, I added the Cache middleware but nothing is cached?")
This is a decision that means that we have to regenerate the entire dynamic response to md5-hash it to check if it has changed. In other terms, it saves nothing at all for the origin server, it only reduces network bandwidth... except that this PR doesn't do that either. We're expected to setup a reverse proxy ourselves. The current implementation should be renamed to "SetSomeCachingHeaders", because that is all it does.
First paragraph in RFC 7232 about generation of Last-Modified. I know that I am old and bitter; but you're not suggesting things to a third party package that people may opt-in to use. You're suggesting things into a framework that has at least half a million installations (quick search at builtwith.com). We need complete implementations, lots of QA and be certain that what is merged is correct and maintainable. |
I've opened this PR to discuss it, don't worry ;) |
29a3e96
to
6a1480a
Compare
Here is a new implementation:
WDYT @sisve |
bb3cb97
to
9d6001b
Compare
Btw:
From the same RFC:
And according to MDN, all modern browsers support And regarding the PR description, I say "easily set HTTP cache headers", it doesn't say that it adds a cache layer. It's basic HTTP knowledge that cache headers must be handled by clients and or proxies. Anyway, the new implementation automatically sets the 304 status code when appropriate (to save bandwidth), and allow to set manually a etag. |
I believe we have different views on how much the average Laravel developer knows about "basic HTTP knowledge", but that could just be my usual bitterness talking. I prefer this newer implementation, mostly because it actually does what can be expected from a middleware. I like that the parameters are optional, and if I don't specify them I will fallback to what the controller provided in the response. This allows me to build custom etags or |
We had some WTF with caching responses containing only headers, but indeed this check can be removed. |
Why do we need to copy their code? Why not load the package in? |
TBH, I do feel this is better off as a laravel package, and not in the core. |
Same here. |
@dunglas thanks for the PR. What made you want to PR this into Laravel? Are you working on some feature related to Laravel that needed this? |
Hi folks, @GrahamCampbell the API Platform's listener works with the Symfony kernel, it is not (yet) compatible with Laravel. @taylorotwell I'm working on a Laravel project and have to set some cache headers on a per route-group basis. |
if these are set, do they take precedence over caching headers set on the web server? |
@browner12 it depends of the configuration of your web server / proxy. Generally speaking, it's better to let the application managing the caching strategy, because it have to more data and logic than the web server. |
oh gotcha. I think I was getting this confused with setting cache headers for assets like CSS, JS, SVG, etc. This would be caching the response (html page)? |
This will tell the client (max_age) or proxies (s_maxage) to cache the response. There is no builtin cache layer (it should be possible to add something like https://symfony.com/doc/master/http_cache.html#symfony-reverse-proxy, but it will always be slower than a dedicated tool coded in C such as Varnish). |
I'm generally OK with this since it doesn't really affect default applications and is opt-in. |
Renamed to |
You should be able to cache it with https://github.com/barryvdh/laravel-httpcache whichs uses he mentioned Symfony caching :) |
This is a port of the API Platform's HTTP cache listener.
It allows to easily set HTTP cache headers through a dedicated middleware:
With this config, the following
Cache-Control
header will be added:max-age=180, public, s-maxage=60
(180 seconds of client-side cache, can be cached by proxies during 60 seconds) and aE-Tag
will be generated.