Brakujące nagłówki przy 'stale-while-revalidate' HTTP Cache
Nagłówek HTTP Cache-Control daje szerokie pole do popisu przy optymalizacji aplikacji internetowych. Jednymi ze względnie ciekawych jego wartości są atrybuty stale-while-revalidate oraz stale-if-error, których sposób działania opisany został w specyfikacji RFC 5861. Już od dwóch lat jego obsługa tego pierwszego zaimplementowana jest w najpopularniejszych przeglądarkach (z wyjątkiem Safari).
Interesującą funkcją atrybutu stale-while-revalidate nagłówka Cache-Control jest możliwość natychmiastowego otrzymania odpowiedzi z pamięci podręcznej przeglądarki z jednoczesnym jej odświeżeniem. Szczególnie, gdy na drodze zapytania mamy do czynienia z wieloma serwerami pośredniczącymi (które obsługują pamięć HTTP), opcja ta, niewielkim kosztem, może znacząco poprawić UX (user experience).
Funkcjonalność świetnie sprawdza się w przypadku standardowych zasobów typu pliki stylów bądź skrypty. W przypadku web serwisów możemy dodatkowo skorzystać z nagłówka 'Vary' jeśli odpowiedź może znacznie różnić się w zależności od nagłówka zapytania. Niestety jak się okazuje, w obecnej wersji Firefoxa, możemy natknąć się na pewne problemy z zapytaniem odświeżającym pamięć podręczną.
Rewalidacja
W przypadku przeglądarek wywodzących się z Chromium: Chrome (97.0.4692.99), Edge (97.0.1072.76) i Chrome (83.0.4254.27) obsługa atrybutu stale-while-revalidate wygląda podobnie. Pierwsze zapytanie spełniające warunki specyfikacji zwraca poprzednio zapamiętaną odpowiedź. Zapytanie w tle jest kopią oryginalnego zapytania i odświeża pamięć, dokładnie tak jak moglibyśmy zakładać.
Firefox (96.0.3) działa tutaj nieco inaczej. Zapytanie w tle co prawda trafia do oczekiwanego zasobu HTTP, jest jednak pozbawione nagłówków oryginalnego zapytania, które zastąpione jest żądaniem.
Jeśli nasz web serwis zwraca odpowiedź zależną od jakiegoś nagłówka ustawianego przez aplikację, klient może otrzymać nieoczekiwaną odpowiedź, np. w innym języku, bądź nawet błąd. Co jednak gdy właściwie dla tej sytuacji zaaplikujemy nagłówek odpowiedzi Vary? Otóż przeglądarki bazujące na Chromium zachowają się tak jak poprzednio.
Firefox z kolei poprawnie wykryje różnicę pomiędzy brakującymi nagłówkami zapytania w tle oraz oryginalnego żądania serwując poprzednią odpowiedź. Przy następnym zapytaniu zachowa się już, jakby pamięć dla tego zapytania była pusta i odpyta serwer właściwym żądaniem, zwracając ostatecznie aktualną odpowiedź.
W obu przypadkach mamy więc negatywne skutki przy użyciu Firefoxa – otrzymanie potencjalnie niewłaściwej odpowiedzi bądź nieciągłe wykorzystanie pamięci podręcznej, w co drugim zapytaniu. Na obecną chwilę warto mieć na uwadze to zachowanie przy wykorzystaniu pamięci podręcznej HTTP. Szczególnie w web serwisach wykorzystujących nagłówki.
Poniżej zamieszczam kod funkcji lambda imitującej web serwis oraz prosty test, który możesz przeprowadzić w swojej przeglądarce.
exports.handler = async function (event) {
const lang = event.headers['accept-language'] || 'en'
const vary = event['queryStringParameters'].vary
const response = {
statusCode: 200,
headers: {
"Cache-Control": "max-age=1, stale-while-revalidate=60",
"Access-Control-Allow-Origin": "*",
"X-Robots-Tag": "noindex",
},
body: (lang === "de" ? "Hallo Welt!" : lang === "pl" ? "Witaj Świecie!" : "Hello World!")
+ " " + new Date().getTime(),
};
if (vary) {
response.headers["Vary"] = "Accept-Language";
}
return response;
}
Z mojego punktu widzenia zapytanie w tle w przypadku Firefoxa nie wygląda na zgodne z ogólną specyfikacją, a w szczególności pod względem metody fetch, z której skorzystałem w teście jako alternatywy do XHR. Jeśli korzystamy z innych klientów, to warto zweryfikować implementację w tym zakresie.
Sprawdziłem z ciekawości rozszerzenie HTTP Cache biblioteki Apache HTTP Client 4.5.13 (Java) i zachowanie było w tym przypadku zbieżne z przeglądarką Chrome. Na szczególną uwagę zasługuje tu też możliwość wykorzystania atrybutu stale-if-error, którego obsługa na dzień dzisiejszy nie jest zaimplementowana w przeglądarkach.