Axios Interceptors in JavaScript: Complete Guide

Axios interceptors let you inspect or change every request and response before your code sees them. Learn how to register, chain, and remove interceptors with real examples.

8 min read

An Axios interceptor is a function that runs automatically before a request leaves your app, or before a response reaches your calling code. You register it once on an Axios instance, and it applies to every request or response that passes through that instance from then on.

The Axios HTTP client guide covers interceptors briefly as one of many features among timeouts and cancellation. This article goes deeper into just interceptors: how they run, in what order, and how to use them for auth tokens and retrying an expired session.

Here is a request interceptor that logs every outgoing URL before the request is sent, then returns the config so the request can continue normally:

javascriptjavascript
import axios from "axios";
 
const api = axios.create({ baseURL: "https://api.example.com" });
 
api.interceptors.request.use((config) => {
  console.log("Sending request to:", config.url);
  // Console shows: Sending request to: /products
  return config;
});

The interceptor receives the request config object, logs the URL, then returns it unchanged. If the function does not return the config, Axios has nothing valid to send over the network.

Request Interceptors

A request interceptor takes two functions, one for a successful config and one for a rejected request, and it runs after you call a method like get or post, but before the network call actually happens. The example below attaches an authentication token to every outgoing request instead of repeating that header at every call site. It targets a browser environment specifically, since localStorage is not available in Node.js; a Node app would read the token from memory or an environment variable instead:

javascriptjavascript
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem("authToken");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

This interceptor runs before every request made through api, so no individual call needs to know the token exists. The config object holds everything about the outgoing request, including its URL, headers, and body, and whatever the success handler returns becomes the final request that gets sent.

Response Interceptors

A response interceptor also takes two functions, one for successful responses in the 2xx range, one for errors, which include both network failures and non-2xx status codes. The next example redirects the user once a session has expired:

javascriptjavascript
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      console.log("Session expired, redirecting to login");
    }
    return Promise.reject(error);
  }
);

Axios treats any status outside the 2xx range as an error by default, so a 401 response lands in the second function here rather than the first. Handling the redirect in this single place means every API call in the app gets the same behavior, instead of each call site checking the response status on its own.

Execution Order with Multiple Interceptors

Request and response interceptors run in opposite orders from each other, which matters once you register more than one on the same instance.

Axios interceptor execution order

Request interceptors run in reverse registration order, so the one you added most recently runs closest to the network call. Response interceptors run in the order you registered them, so the first one you added sees the response before any later ones do. This is the opposite of what many developers expect on first read, so it helps to log a message inside each interceptor while testing if the exact order matters for your logic.

Removing an Interceptor with Eject

Registering an interceptor returns a numeric id. Passing that id to the eject method removes only that one interceptor, leaving every other registered interceptor untouched:

javascriptjavascript
const loggerId = api.interceptors.request.use((config) => {
  console.log("Request:", config.url);
  return config;
});
 
api.interceptors.request.eject(loggerId);

After this call, requests made through api no longer trigger the logger. Store the returned id in a variable at registration time, since there is no other way to target that specific interceptor for removal later.

Practical Example: Retrying After a Token Refresh

A common real use of interceptors is refreshing an expired access token and retrying the original request automatically, instead of forcing the user to log in again over a single expired token. This is one narrow case of a broader idea covered in API retry patterns in JavaScript. The _retry flag below stops the code from refreshing more than once per request:

javascriptjavascript
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      const { data } = await axios.post("/auth/refresh");
      localStorage.setItem("authToken", data.accessToken);
      return api(originalRequest);
    }
    return Promise.reject(error);
  }
);

error.config holds the original failed request, so calling api(originalRequest) resends it once the new token is stored. Without the _retry flag, a permanently invalid token would trigger an endless loop of refresh attempts, since every retried request would fail with 401 again.

Common Mistakes

MistakeWhy it breaksFix
Forgetting to return config in a request interceptorAxios sends an undefined config instead of the real requestAlways return config at the end of the success handler
Forgetting to reject the error in an error handlerThe error is swallowed and the calling code's catch block never runsReturn a rejected promise unless you are intentionally recovering
Registering interceptors on the shared global axios objectEvery part of a large app is affected, including code that expects different behaviorCreate a dedicated instance and register interceptors on that instance
Missing a retry flag in a token refresh interceptorA permanently invalid token causes an infinite refresh loopMark the request so the refresh only happens once

When to Reach for an Interceptor

Use an interceptor when the same logic needs to run for every request or response on an instance, such as attaching a token, logging, or refreshing a session. For logic that only applies to a single call, pass it directly in that call's own config instead of adding a global interceptor every other request also has to pass through.

Rune AI

Rune AI

Key Insights

  • A request interceptor runs before every outgoing request; a response interceptor runs before every response reaches your code.
  • Register interceptors with axios.interceptors.request.use() and axios.interceptors.response.use(), each taking a success handler and an error handler.
  • Request interceptors run in reverse registration order; response interceptors run in the order they were registered.
  • Store the id returned when registering an interceptor and pass it to eject() to remove that one interceptor.
  • Interceptors are the standard place for attaching auth tokens, logging, and handling token refresh on 401 responses.
RunePowered by Rune AI

Frequently Asked Questions

Do interceptors run for every request on an Axios instance?

Yes. Once registered, a request or response interceptor runs for every call made through that specific Axios instance, not just one call.

What order do multiple interceptors run in?

Request interceptors run in reverse order, so the last one registered runs first, right before the request is sent. Response interceptors run in the order they were registered.

Can an interceptor stop a request from being sent?

Yes. Rejecting inside a request interceptor cancels that request, and the rejection flows into the response error handler and then into the original catch block.

Does axios interceptors work the same in Node.js as in the browser?

Yes. Axios interceptors run through the same JavaScript code in both environments. Only the underlying transport differs, XMLHttpRequest in the browser and the http module in Node.js, which does not affect interceptor behavior.

Conclusion

Interceptors turn repeated per-call logic, like attaching a token or logging every response, into a single place that runs automatically for every request. Register them once on an Axios instance, keep the success and error handlers focused, and remove them with eject when they are no longer needed.