Brakujące nagłówki przy 'stale-while-revalidate' HTTP Cache

Autor
Damian
Terlecki
6 minut
Inne

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ć.

Zapytania na skutek odpowiedzi z atrybutem 'stale-while-revalidate' nagłówka 'Cache-Control' w zakładce sieć przeglądarki Chrome Zapytania odświeżające pamięć podręczną w tle w zakładce sieć przeglądarki Chrome

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.

Zapytania odświeżające pamięć podręczną w tle w zakładce sieć przeglądarki Firefox (brakujące nagłówki)

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ź.

Zapytania odświeżające pamięć podręczną na pierwszym planie w zakładce sieć przeglądarki Firefox (spowodowane brakującymi nagłówkami zapytania w tle)

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.