Request interceptor mutates config causing unexpected header in subsequent interceptor
0 reputation · 08 Jun 2023, 04:19 UTC
0 reputation · 08 Jun 2023, 04:19 UTC
Determine whether Axios provides an isolated config object for each request interceptor so that modifications made by one interceptor do not propagate to others in the chain.
According to the Axios documentation, request interceptors are executed in the order they are added, but it does not state whether the config argument is a fresh copy or a shared reference. If the same config object is mutated by an early interceptor, later interceptors may see the altered state, which can lead to unintended headers, authentication tokens, or base‑URL changes.
Specific questions:
34775 reputation · 08 Jun 2023, 11:02 UTC
Axios passes the same config object through every request interceptor. No shallow or deep copy is made automatically. Consequently, a mutation performed by an early interceptor is visible to all subsequent interceptors and the final request.
baseURL, or setting timeout in one interceptor will affect later logic.axios.interceptors.request.use(config => {
config.headers = {
...config.headers,
'X-Custom-Header': 'value'
};
return config;
});
axios.interceptors.request.use(original => {
const cloned = { ...original, headers: { ...original.headers } };
cloned.headers['X-Custom-Header'] = 'value';
return cloned;
});
config.data or other nested structures, clone those parts as well to prevent accidental side‑effects.config.custom) instead of altering shared properties like headers or params.
config.headers. The header should appear in the log.config === nextConfig inside each interceptor to confirm the same reference is passed.Do any of your interceptors perform asynchronous operations (e.g., fetching a token from a remote service) that return a Promise? If so, the cloning strategy may need to be adjusted to handle the asynchronous flow correctly.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 08 Jun 2023, 15:03 UTC
Axios passes the same config object reference through every request interceptor; it does not create a per-interceptor copy. Because headers is nested, even { ...config } leaves the original headers object shared. A downstream interceptor reading config.headers will see upstream changes, including unexpected authorization or custom headers.
Clone both levels before mutating:
axios.interceptors.request.use((config) => {
const next = { ...config, headers: { ...config.headers } };
next.headers['X-Request-Id'] = 'unique-id';
return next;
});Verify by adding one interceptor that sets a header and a second that logs config.headers; the header appears unless the first interceptor clones headers first.