Search code examples
httpcachinghttpsgraphqlhttp-headers

Difference between public, max-age and s-maxage


TLDR:

What is the difference between the Public, max-age=<VALUE> and maxage=<VALUE>, s-maxage=<VALUE> cache-control syntax?

Question:

For one of my projects, I am looking to reduce the server load via the Cache-control http-header. This project is hosted as a serverless function on Vercel and calls GitHub's GraphQL API on the backend to retrieve GitHub user information. Both the Vercel API and GitHub API are rate limited, so I am looking for the best header to prevent these limits from being hit. To achieve this, I am currently using the following Cache-control header:

public, max-age=14400, stale-while-revalidate=86400

According to the Mozilla documentation, this header should keep both the private browser cache and server cache fresh for 4 hours, while the stale cache can be reused for 1 day while it revalidates on the server. Furthermore, since I am not using an authentication header, I should be able even to remove the Public keyword.

The Vercel cache documentation, however, recommends the following header:

maxage=0, s-maxage=14400, stale-while-revalidate=86400

Based on the Vercel documentation and this Stack Overflow question, I, therefore, think the following header is best suited for reducing both the Vercel and GitHub load:

maxage=14400, s-maxage=7200, stale-while-revalidate=86400

To my understanding, with this header, the cache will be fresh for 4 hours for individual users while the Vercel server refreshes the cache every 2 hours, and a stale cache can be reused for 1 day while it revalidates on the server.

As I am uncertain about the difference between the Public, max-age=<VALUE> and maxage=<VALUE>, s-maxage=<VALUE> syntax, I quickly wanted to double-check my understanding.

According to the Mozilla documentation, these two syntaxes should result in the same behaviour if the <VALUE> is equal between maxage and s-maxage properties. However, the Symfony documentation states that the s-maxage flag prohibits a cache from using a stale response in stale-if-error scenarios. My question is, therefore: What is the exact difference between these two syntaxes, and which one would you recommend for reducing the load on both the Vercel and GitHub api?


Solution

  • The Vercel-recommended cache headers are well-suited to minimizing the number of API calls:

    Cache-Control: max-age=0, s-maxage=N
    

    The s-maxage is used to control caching by the Vercel Edge Network, allowing it to serve cached responses rather than call the serverless function. As far as I know it does not have rate limits of its own.

    Of course, you probably have other caching goals besides just reducing API calls. So you might want to use max-age as well to allow browser caching to reduce latency for your users.

    It's also a good idea to use stale-while-revalidate, but note that this is just a mechanism to reduce latency. The revalidation still happens, so it won't have an effect on the number of API calls.

    As for public, it means "that any cache MAY store the response, even if the response would normally be non-cacheable or cacheable only within a private cache". If your response is already cacheable by a public cache then this directive won't have any effect.