Skip to content

Request specifics ​

Each request can customize its caching through the cache property.

TIP

Every request-specific property can also be passed to setupCache, the same way as the global options, to change the default for all requests.

id ​

optional

A custom Request ID for this request. When it is not set, the key generator creates one from the request properties.

cache ​

optional
  • Type: Partial<CacheProperties<R, D>> | false
  • Default: {} (inherits from the global configuration)

TIP

Any property you leave out inherits its value from the global configuration.

The cache property is where all per-request cache customization happens. Pass an object with the cache properties you want to change for this request.

To disable caching for a specific request, use cache: { enabled: false }. Disabling the cache does not remove an existing entry from the storage:

ts
// Make a request with cache disabled
const { id: requestId } = await axios.get('url', {
  cache: { enabled: false }
});

// Delete the cache entry for this request if needed
await axios.storage.remove(requestId);

cache: false still works for backward compatibility, but it will be removed in the next major release. Use cache: { enabled: false } instead.

cache.enabled ​

optional
  • Type: boolean
  • Default: true

Whether the cache is enabled for this request. When set to false, the request skips the cache entirely.

Set it to false globally and true per request for an opt-in cache.

Example: opt-in cache ​

Disable the cache by default and enable it only for specific endpoints:

ts
import { setupCache } from 'axios-cache-interceptor';

// Cache is disabled by default
const axios = setupCache(axiosInstance, {
  enabled: false
});

// Most requests won't use the cache
await axios.get('/api/realtime-data'); // Not cached

// Enable the cache for specific heavy or expensive requests
await axios.get('/api/heavy-computation', {
  cache: {
    enabled: true,
    ttl: 1000 * 60 * 10 // Cache for 10 minutes
  }
}); // Cached

Example: opt-out cache ​

The cache is enabled by default and disabled for specific endpoints:

ts
import { setupCache } from 'axios-cache-interceptor';

// Cache is enabled by default (you can omit this option)
const axios = setupCache(axiosInstance, {
  enabled: true
});

// Most requests use the cache
await axios.get('/api/user-profile'); // Cached

// Disable the cache for specific real-time endpoints
await axios.get('/api/live-stock-prices', {
  cache: { enabled: false }
}); // Not cached

cache.ttl ​

optional
  • Type: number | ((response: CacheAxiosResponse) => number | Promise<number>)
  • Default: 1000 * 60 * 5 (5 minutes)

WARNING

When interpretHeader is enabled, this value is only used when the response headers don’t determine a TTL.

The time, in milliseconds, until the cached value expires.

If you pass a function, it receives the complete response and must return the TTL.

The ttl is only applied when the response is first cached; changing it on later requests has no effect on the existing entry. See #1024 for workarounds.

cache.interpretHeader ​

optional
  • Type: boolean
  • Default: true

When enabled, the ttl is inferred from the response headers, following the MDN docs and the HTTP specification. Responses with Cache-Control: no-cache or no-store are not cached.

See the interpretHeader implementation for more information.

cache.cacheTakeover ​

optional
  • Type: boolean
  • Default: true

Most of this library’s caching depends on standard HTTP headers, and browsers use the same headers for their own cache.

You can see the browser cache in the network tab of your browser’s dev tools.

Network tab in Chrome DevTools

When your routes include Cache-Control in their responses, both the library and your browser may cache the response, resulting in a double layer of cache.

This option adds headers to the request that tell any client or adapter not to cache the response, so only the library caches it. Headers you already set on the request are not overwritten.

These headers are only added to this request and don’t affect any other request or response the server handles.

Headers included:

  • Cache-Control: no-cache, no-store, must-revalidate, max-age=0
  • Pragma: no-cache
  • Expires: 0

Safari compatibility

The max-age=0 directive is included for Safari (including iOS Safari), which has historically cached more aggressively and may ignore no-cache on its own.

Alternative

cacheTakeover works in most browsers, according to this StackOverflow answer, but it may be unreliable in rare cases because of browser-specific behavior or network intermediaries.

For maximum reliability, add a random query parameter instead:

ts
axios.get(
  `/api/data?cachebuster=${Math.random().toString(36).slice(2)}`,
  {
    id: 'api-data-endpoint' // Keeps the same cache key even though the URL changes
  }
);

Your backend can ignore the cachebuster value. This guarantees the browser won’t cache the response while axios-cache-interceptor keeps working as usual.

CORS considerations

This option does not work for CORS requests when the server doesn’t allow these headers. The browser throws: Request header field Pragma is not allowed by Access-Control-Allow-Headers in preflight response.

To fix it, add Cache-Control, Pragma and Expires to the Access-Control-Allow-Headers of your server’s CORS configuration.

If you cannot change the CORS configuration, you can:

  1. Disable this option (cacheTakeover: false).
  2. Use the query parameter approach described above.

Learn more about why this option is enabled by default in #437 and in this StackOverflow answer.

cache.methods ​

optional
  • Type: Method[]
  • Default: ["get", "head"]

The HTTP methods that should be cached. The default only caches GET and HEAD; use this option to enable caching for POST, PUT, DELETE and other methods.

To enable caching for POST requests:

ts
// Enables caching for POST requests globally
const axios = setupCache(instance, {
  methods: ['get', 'post']
});

// Or just for this request
axios.post('url', data, {
  cache: {
    methods: ['post']
  }
});

methods is a per-request option because some requests are exceptions to the global rule.

cache.cachePredicate ​

optional
  • Type: CachePredicate<R, D>
  • Default: { statusCheck: (status) => [200, 203, 300, 301, 302, 404, 405, 410, 414, 501].includes(status) }(the cacheable status codes defined by RFC 7231)

An object, or a function that receives the response, that decides whether a response can be cached. The object form accepts:

  • statusCheck: caches only if this function returns true for the response status.
  • containsHeaders: caches only if every function returns true for its response header. Header names are lowercase in axios; a missing header calls the function with undefined.
  • responseMatch: caches only if this function returns true for the response.
  • ignoreUrls: never caches requests whose URL matches any of these patterns.
  • allowUrls: only caches requests whose URL matches at least one of these patterns.

ignoreUrls and allowUrls are tested against config.url only (baseURL is not included) before the request is sent. A string pattern matches when the URL contains it. If a URL matches both lists, ignoreUrls takes precedence. When the request has no url (only a baseURL), both lists are ignored.

ts
axios.get<{ auth: { status: string } }>('url', {
  cache: {
    cachePredicate: {
      // Only cache if the response comes with a "good" status code
      statusCheck: (status) => true, // some calculation

      // Tests against any header present in the response
      containsHeaders: {
        'x-custom-header-3': (value) => true // some calculation
      },

      // Checks the response body
      responseMatch: ({ data }) => {
        // Only caches authenticated responses
        return data.auth.status === 'authenticated';
      },

      // Never caches requests whose URL starts with "/api"
      ignoreUrls: [/^\/api/],

      // Only caches requests whose URL includes "weekly"
      allowUrls: ['weekly']
    }
  }
});

cache.update ​

optional
  • Type: CacheUpdater<R, D>
  • Default: {}

When this request succeeds with a network response (not a cache hit), it updates or deletes the cache entries of other requests. It is a Record keyed by request ID, where each value is either:

  • 'delete': removes that cache entry.
  • a function that receives the current cache entry and this request’s response, and returns the new cache entry, 'delete' to remove it or 'ignore' to leave it unchanged. Entries that are still loading are skipped.

You can also pass a single function instead of a record. It receives the response and can do anything, but this is not recommended: it is simpler to consume the response normally and write your own code after it.

ts
// Request IDs used by other requests
let profileInfoId: string;
let userInfoId: string;

axios.post<{ auth: { user: User } }>(
  'login',
  { username, password },
  {
    cache: {
      update: {
        // The user is now authenticated, so the profile info must be fetched again
        [profileInfoId]: 'delete',

        // Writes the logged-in user into the cached user info response
        [userInfoId]: (cachedValue, response) => {
          if (cachedValue.state !== 'cached') {
            // Only update entries that are cached
            return 'ignore';
          }

          cachedValue.data.data = response.data.auth.user;

          // This value is stored and returned by later requests to this cache entry
          return cachedValue;
        }
      }
    }
  }
);

See Invalidating cache for more examples.

cache.etag ​

optional
  • Type: string | boolean
  • Default: true

Configures ETag and If-None-Match handling for cache revalidation.

  • true: stores the response’s ETag and sends it in If-None-Match when revalidating a stale entry.
  • string: sends this static value in If-None-Match instead.
  • false: disables ETag handling.

A cached entry with an ETag becomes stale instead of being removed when its TTL expires. A 304 Not Modified response reuses the stored data and refreshes the entry.

cache.modifiedSince ​

optional
  • Type: Date | boolean
  • Default: false (true when etag: false is passed to setupCache)

Sends the If-Modified-Since header when revalidating a stale entry. Use a Date to send a custom static value, or true to derive it from the cached response.

If the request was never cached before, the header is not set.

With true, the value of the response’s Last-Modified header is used when present; otherwise, the cache creation timestamp is sent.

cache.staleIfError ​

optional
  • Type: number | boolean | StaleIfErrorPredicate<R, D>
  • Default: true

When a request that revalidates a stale cache entry fails, the library returns the stale data instead of the error. This covers network errors, rejected status codes and similar failures. The returned response has cached: true and stale: true.

  • true: returns the stale data. If the error response has a Cache-Control: stale-if-error=N header, only within N seconds after the entry was cached.
  • number: the maximum age, in milliseconds, of the stale data that can be returned.
  • false: disables this behavior.
  • a function: receives the error response (if any), the current cache entry (in the loading state, holding the stale data) and the error, and returns one of the values above.

WARNING

A response is treated as an error when its status is rejected by axios’s validateStatus. In that case, if staleIfError resolves to true (or to a number that still covers the entry), the stale cache is returned instead of the server’s “invalid” response.

To keep the server’s response, use a predicate that only allows stale data when there is no response:

ts
const customPredicate = (response, cache, error) => {
  // Only returns stale data when there is no response (network errors)
  return !response;

  // Requests with a response still follow axios's default behavior
  // and throw an error, but the error keeps the response
};

axios.get('/api/data', {
  cache: {
    staleIfError: customPredicate
  }
});

cache.override ​

optional
  • Type: boolean
  • Default: false

Bypasses the current cache and always makes a new HTTP request. The current cache entry is not deleted; it is replaced when the response arrives.

Unlike cache: { enabled: false }, this does not disable the cache. It only skips the checks made before the request, so all post-request options still work as expected. An overriding request does not wait for concurrent requests to the same key and does not send If-None-Match or If-Modified-Since headers.

cache.vary ​

optional
  • Type: string[] | boolean
  • Default: true

Configures how the HTTP Vary header is handled.

  • true: handles Vary automatically (default, recommended).
  • false: disables Vary checks (WARNING: can cause cache poisoning).
  • string[]: uses these request headers instead of the server’s Vary list. Their values are always part of the cache key, whether set globally or per request, even when the response has no Vary header.

When the server responds with a Vary header, the library stores the values of the listed request headers with the cache entry. A later request with different values for those headers gets its own cache entry:

ts
// Request with an Authorization header
await axios.get('/api/users', {
  headers: { authorization: 'Bearer token-A' }
});
// Server responds with: Vary: Authorization
// The entry stores { authorization: 'Bearer token-A' } alongside the response

// Different authorization = different cache entry
await axios.get('/api/users', {
  headers: { authorization: 'Bearer token-B' }
});
// The Authorization value does not match, so this request switches to a
// Vary-aware ID that includes its header value and makes its own request

A response with Vary: * is stored as stale right away, so every later request revalidates it with the server.

cache.hydrate ​

optional
  • Type: undefined | ((cache: CachedStorageValue | StaleStorageValue | LoadingStorageValue) => void | Promise<void>)
  • Default: undefined

Called when a network request is needed but an older, probably expired, cache entry exists. It receives the current cache entry BEFORE the network request starts, so you can update your UI with the old data in the meantime. The request waits for the returned promise, so keep this function fast.

This avoids flickering the UI, especially on slow networks or with a persisted cache in mobile apps.

WARNING

If the request is answered from a valid cache, no network request is made and hydrateIS NOT CALLED, as the axios promise resolves right away. It is also not called for entries that must be revalidated (Cache-Control: must-revalidate).

ts
// Example of a function that renders data on the screen
function render(data) {}

const response = await axios.get(/* [!code focus:10] */ 'url', {
  // Called right away if axios needs to make a network request
  cache: {
    hydrate: (cache) => render(cache.data.data)
  }
});

// After the network request ends, re-render the UI
// with the fresh data
render(response.data);

Made with ❤️