Symfony HTTP Cache invalidation - symfony

Here's the deal. Symfony 2.8 setup. I want to serve cached rendered content with Symfony HTTP Cache (content rarely changes) and to be able to invalidate it whenever I want.
When I use Symfony HTTP cache like this:
$response = new JsonResponse($data, Response::HTTP_OK);
$response->setPublic();
$response->setSharedMaxAge(3600);
$response->headers->addCacheControlDirective('must-revalidate', true);
$response->setVary('Accept-Encoding');
It results in following headers:
Cache-Control:must-revalidate, public, s-maxage=3600
vary:Accept-Encoding
Content gets cached, but because of cache control set to public, intermediate proxies can cache it as well and obviously I can't invalidate data there.
On the other hand setting cache control to private will force Symfony HTTP cache to not store it.
With Varnish (which I can't use in this case) I would send Cache-Control: public from the app server and overwrite it to Cache-Control: private with VCL rule, so browsers always return to check for a fresh version.
I wonder if I should do something similar with Symfony HTTP Cache (override Cache-Control header once response passes caching layer) or is there a simpler way?

Related

Next-App deployed on Vercel: Request for new refresh-token leads to unwanted 304 Not Modified-response

I built a Next-app on the frontend and deployed it on Vercel (and a Django-app on the backend deployed on Heroku). Authentication is implemented with JWTs and and a Next-API route connecting the Next-Frontend with the Django-Backend.
The authentication workflow relevant for my problem is the following:
Whenever an authenticated user visits a protected page, a request (with the current refresh-token) is sent to the backend, asking for a fresh pair of access & refresh tokens. This works perfectly fine when testing locally the production-optimized version of the Nextjs-app (via next build & next start): I receive a 200 OK response with the new refresh- and access-tokens.
However - when I do the same thing in my Vercel-deployed app, after the first request/response-cycle for new tokens (which works as expected), I always get back a 304 Not Modified-response, which does not have any cookies in its Headers. I can see that my Django-server sends off the new tokens but somehow they get lost on the Next-server.
I did some research and if I understand correctly, Vercel has some caching rules, that lead to this behavior. However, I could not find any solution on how to avoid this from happening.
Does anyone know, what to do in this case?
I figured it out by myself, sharing the solution for anyone who might run into this problem in the future. When it comes to caching - Vercel's production servers behave differently than the test server you can run with npm run start. While the former caches certain responses by default for performance reasons, the latter does not do that.
To deactivate the caching for the response giving me the unwanted 304 Not Modified status, i simply added a Cache-Control-header with no-cache, no-store, max-age=0 and must-revalidate directives to it:
export default async function refresh(req, res) {
...
if (apiRes.status === 200) {
// "Cache-Control"-header needed for production to avoid Vercel's default
// response caching mechanism
res.setHeader(
"Cache-Control",
"no-cache, no-store, max-age=0, must-revalidate"
);
...

URLSession and URLCache not working as expected

For a GET request which is consistently the same (it has a query param too), I'm returning in the response this response header.
Cache-Control: max-age=3600, private
I'm creating a URLSession with this configuration. Basically, ensuring my cache is large. The URLSession persists throughout the life of the app and all requests go through it.
 var configuration = URLSessionConfiguration.default  
 let cache = URLCache(memoryCapacity: 500_000_000, diskCapacity: 1_000_000_000)
 configuration.urlCache = cache
 configuration.requestCachePolicy = .useProtocolCachePolicy
I create a data task with the completion handler:
let task = session.dataTask(with: request) { data, response, error in
//... my response handling code here
}
task.resume()
There isn't anything complicated or unusual I'm doing. I'm now trying to optimise some calls with a caching policy.
So I have 2 issues:
Requests that have no-cache policy are still being saved into the URLCache. Which I've confirmed by looking into the URLCache . Why is it caching them when the response explicitly states no-cache policy and I've told the session to use the protocol policy.
On the request where I do want to use the cache, when I call session.dataTask on subsequent times, it never fetches from the cache. It always hits the server again - which I can confirm from the server logs.
What piece of this am I missing here?

Does setting cache control to public prevent private cache?

Can you enable both private and public caching through the cache-control header? How would you do that?
Thanks!
The Cache-Control: public response header enables any kind of intermediate cache. If you want the response to be cached as widely as possible, then this is what you use.
The Cache-Control: private response header disables any caching that might expose the response to any user but the current user. This will prevent caching by most proxies and intermediate servers, allowing the response to be cached only in user-specific client-side caches. Any cache that can be used with this setting can also be used with the public setting.

Caching a JSON response using ETag in a React Native app

What is the best way to implement the following scenario in a React Native app?
Make an HTTP request to the server, get a JSON response and an ETag header.
Save this JSON response in a way that will persist even after the app is restarted by the user.
Whenever this HTTP request is repeated, send an If-None-Match header.
When you get a "Not Modified" response, use the version in the persisted cache.
When you get a "Successful" response (meaning the response has changed), invalidate the persisted cache, save the new response.
Does React Native have a component that does these things out of the box? If not, what is the most common way people use to handle this?
The fetch() API of React native is following the http caching spec and it provides this feature. When you hit a 304 a 200 old response will be found in the cache and be reused.
Details:
https://github.com/heroku/react-refetch/issues/142
As answered at: https://stackoverflow.com/a/51905151
React Native’s fetch API bridges to NSURLSession on iOS and okhttp3 on Android. Both of these libraries strictly follow the HTTP caching spec. The caching behavior will depend primarily on the Cache-Control and Expires headers in the HTTP response. Each of these libraries have their own configuration you can adjust, for example to control the cache size or to disable caching.
And this: How to use NSURLSession to determine if resource has changed?
The caching provided by NSURLSession via NSURLCache is transparent, meaning when you request a previously cached resource NSURLSession will call the completion handlers/delegates as if a 200 response occurred.
If the cached response has expired then NSURLSession will send a new request to the origin server, but will include the If-Modified-Since and If-None-Match headers using the Last-Modified and Etag entity headers in the cached (though expired) result; this behavior is built in, you don't have to do anything besides enable caching. If the origin server returns a 304 (Not Modified), then NSURLSession will transform this to a 200 response the application (making it look like you fetched a new copy of the resource, even though it was still served from the cache).
Oof. Been over a year. I assume you know this is a resounding "no," right? You'll have to parse the response headers to grab the ETag and store that on the device (you're not using the browser) and then add the header to the subsequent requests after retrieving it from your storage mechanism of choice.
I just found this because I was looking to see if anybody had done this in React, let alone React Native, and I'm not seeing anything.
Whelp, time to roll up my sleeves and invent this thing...
okay, this is my current solution, not production tested yet. would love your feed back googlers.
i use Axios, but if you dont you still implement this around what ever wrapper you have around fetch -unless u use native fetch !-
import api from 'your api wrapper.js'
api.etags = new Set;
api.cache = new Set;
api.addRequestTransform(request => {
const etag = api.etags.get(request.url);
if (etag) {
request.headers['HTTP_IF_NONE_MATCH'] = etag;
}
})
// or whatever you use to wrap ur HTT
api.addResponseTransform(response =>{
if (
response.status === 304 &&
response.headers &&
response.headers.etag &&
api.cache.has(response.headers.etag)
) {
console.log('%cOVERRIDING 304', 'color:red;font-size:22px;');
response.status = 200;
response.data = api.cache.get(response.headers.etag);
} else if (response.ok && response.headers && response.headers.etag) {
api.cache.set(response.headers.etag, response.data);
api.etags.set(response.config.url, response.headers.etag);
}
});
what we are doing here is saving response result into api.cache, and saving the etags into api.etag, then we send etag with request every time.
we can upgrade this to also remember the correct status code, or save etags to disk, duno. what do you think :) ?

Is caching in browser automatic?

I have a JavaScript app that sends requests to REST API, the responses from server have cache headers (like ETag, cache-control, expires). Is caching of responses in browser automatic, or the app must implement some sort of mechanism to save the data?
An AJAX request is no different from a normal request - it's a GET/POST/HEAD/whatever request being sent by the browser, and it is handled as such. This is confirmed here:
The HTTP and Cache sub-systems of modern browsers are at a much lower level than Ajax’s XMLHttpRequest object. At this level, the browser doesn’t know or care about Ajax requests. It simply obeys the normal HTTP caching rules based on the response headers returned from the server.
As per the jQuery documentation, caches can also be invalidated in at least one usual way (appending a query string):
cache (default: true, false for dataType 'script' and 'jsonp')
Type: Boolean
If set to false, it will force requested pages not to be cached by the browser. Note: Setting cache to false will only work correctly with HEAD and GET requests. It works by appending "_={timestamp}" to the GET parameters. The parameter is not needed for other types of requests, except in IE8 when a POST is made to a URL that has already been requested by a GET.
So in short, given the same headers, AJAX responses are cached the same way as other requests.
Browser handles automatically cache of resources. What you seem to be asking about is the actuall response from the server.
You will need to set that up yourself in your application. You can do so both on front-end and backend.
Most of JS frameworks have cache control implemented, for example:
jQuery
$.ajaxSetup({
// Disable caching of AJAX responses
cache: false
});
AngularJS
$http.defaults.cache = false;
etc.
On backend it really depends on what language you using, what server engine etc.
Checkout Memcached for example
http://memcached.org/
As with anything with web development there are odd things here and there, for example some IE versions are automatically chaching requests and you have to add unique id to the url to prevent that.
From https://developer.mozilla.org/en-US/docs/AJAX/Getting_Started :
Note 2: If you do not set header Cache-Control: no-cache the browser will cache the response and never re-submit the request, making debugging "challenging." You can also append an always-diferent aditional GET parameter, like the timestamp or a random number (see bypassing the cache)
Browser should handle the cache automatically.
Check this article, there's only clear cache method for javascript.
https://developer.chrome.com/extensions/browsingData
If the server sends the response with any of cache headers browsers should respect it. there is no difference between resources or ajax requests.
also you can specify cache headers in your ajax calls to make it not use caches and fetch the whole response from the server.
Most modern browsers support browser caching because of the cache and expire headers
http://www.arlocarreon.com/blog/http/http-requests-and-your-browsers-cache/
There are 2 apects of an HTTP request that can qualify it for being cached:
- Specifc HTTP cache and expire headers
- A unique URL
Interesting read:
http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/

Resources