How to Make Conditional Axios HTTP Requests
Conditional HTTP requests allow clients to optimize bandwidth and
improve application speed by asking the server to return data only if it
has changed since the last request. This article explains how to
implement conditional requests in Axios using the
If-None-Match (with ETags) and
If-Modified-Since (with timestamps) headers, how to
configure Axios to handle 304 Not Modified status codes,
and how to combine these techniques with client-side caching.
Understanding Conditional Headers
Conditional requests rely on validators received from the initial server response:
- ETag /
If-None-Match: The server returns a unique identifier (hash) in theETagheader. On subsequent requests, the client sends this identifier back using theIf-None-Matchheader. - Last-Modified /
If-Modified-Since: The server sends a timestamp in theLast-Modifiedheader. On subsequent requests, the client sends this date back using theIf-Modified-Sinceheader.
If the resource has not changed, the server responds with an HTTP
status 304 Not Modified and an empty body, signaling that
the client should use its locally stored copy.
Sending Conditional Headers with Axios
You can pass conditional headers directly into the
headers property of the Axios request configuration
object.
import axios from 'axios';
async function fetchResource(url, cachedEtag, cachedLastModified) {
try {
const response = await axios.get(url, {
headers: {
...(cachedEtag && { 'If-None-Match': cachedEtag }),
...(cachedLastModified && { 'If-Modified-Since': cachedLastModified }),
},
// Ensure Axios does not throw an error on 304 status codes
validateStatus: (status) => (status >= 200 && status < 300) || status === 304,
});
if (response.status === 304) {
console.log('Resource not modified. Using cached data.');
return null; // Handle using local cache
}
// Capture headers for future conditional requests
const newEtag = response.headers['etag'];
const newLastModified = response.headers['last-modified'];
return {
data: response.data,
etag: newEtag,
lastModified: newLastModified,
};
} catch (error) {
console.error('Request failed:', error);
throw error;
}
}Handling 304
Status Codes with validateStatus
By default, Axios rejects promises for any HTTP status code outside
the range of 2xx. Because 304 Not Modified
falls under 3xx, Axios will throw an error unless
configured otherwise.
To handle 304 responses gracefully, use the
validateStatus option:
const response = await axios.get('https://api.example.com/data', {
headers: {
'If-None-Match': '"686897696a7c876b7e"',
},
validateStatus: (status) => (status >= 200 && status < 300) || status === 304,
});Implementing a Basic In-Memory Cache
To fully leverage conditional requests, you should store the response
body alongside its validator (ETag or
Last-Modified):
const cache = new Map();
async function getCachedResource(url) {
const cached = cache.get(url);
const headers = {};
if (cached?.etag) {
headers['If-None-Match'] = cached.etag;
}
if (cached?.lastModified) {
headers['If-Modified-Since'] = cached.lastModified;
}
const response = await axios.get(url, {
headers,
validateStatus: (status) => (status >= 200 && status < 300) || status === 304,
});
if (response.status === 304) {
return cached.data;
}
const freshData = {
data: response.data,
etag: response.headers['etag'],
lastModified: response.headers['last-modified'],
};
cache.set(url, freshData);
return freshData.data;
}Automating with Axios Interceptors
If you need conditional requests across your entire application, you
can use Axios interceptors to automatically append stored ETags or
timestamps before requests are sent, and update your cache when
200 OK or 304 Not Modified responses are
received. Alternatively, existing ecosystem libraries such as
axios-cache-interceptor offer built-in conditional request
handling out of the box.