Stop Repeating Your Auth Headers: Centralizing Logic with Axios Interceptors
Stop repeating auth headers in every API call. Learn how to use Axios Interceptors to centralize authentication tokens and global error handling for cleaner, more maintainable code.
04 Oct 2026, 10:19 UTC

The Boilerplate Burden
When building an application that relies on a REST API, you quickly encounter a repetitive pattern: every single request needs an Authorization header, and every single response needs to be checked for a 401 Unauthorized status to trigger a logout. Manually adding these to every axios.get() or axios.post() call is not just tedious—it is a maintenance risk. If your token key changes from Bearer to Token, you have to update every call site in your codebase.
The solution is to move this logic out of your components and into the network layer using Interceptors. Interceptors act as middleware for your HTTP requests, allowing you to modify the configuration before it leaves the browser and transform the response before it reaches your .then() or .catch() blocks.
Handling Requests Globally
A request interceptor allows you to inject logic—such as retrieving a JWT (JSON Web Token) from localStorage—into every outgoing call. Instead of passing the token as a parameter to your API service functions, the interceptor handles it silently in the background.
This ensures that your business logic remains clean. Your components simply ask for /user/profile, and the interceptor ensures the request is authenticated before it hits the wire.
Centralizing Error Recovery
Response interceptors are equally powerful for handling systemic failures. Rather than wrapping every API call in a try/catch block to handle session expiration, you can define a single response interceptor that watches for specific HTTP status codes.
For example, if the server returns a 401, the interceptor can clear the local session and redirect the user to the login page. This creates a "fail-safe" mechanism where the application reacts consistently to authentication failures regardless of which page the user is on.
Implementation: The Auth Instance
To avoid affecting every single request in your app (such as public pings or third-party API calls), it is best practice to create a dedicated Axios instance. This prevents global interceptors from leaking into requests that shouldn't have auth headers.
import axios from 'axios';
// Create a dedicated instance for the API
const api = axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 10000,
});
// Request Interceptor: Attach Token
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Response Interceptor: Handle 401s
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
console.warn('Session expired. Redirecting to login...');
localStorage.removeItem('auth_token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default api;
Verification and Execution
To verify this setup, run the following check in your application logic using the exported api instance:
- Execution: Call
api.get('/protected-route'). - Check: Open the browser Network tab. Inspect the request headers to ensure
Authorization: Bearer [token]is present without being explicitly passed in the function call. - Risk: Ensure the request interceptor always returns the
configobject. If you forget to return it, the request will hang indefinitely because the promise chain is broken.
Trade-offs and Limitations
While interceptors simplify code, they introduce a layer of "hidden" logic. Because the modification happens outside the immediate function call, debugging can become difficult if you have multiple interceptors stacked on top of each other. The order of execution matters: interceptors are processed in the order they were added for requests, but in reverse order for responses.
Additionally, interceptors can lead to unhandled promise rejections if the error handler does not return Promise.reject(error). If you "swallow" the error in the interceptor without rejecting it, the calling function's .catch() block will never execute, making it impossible for the UI to show a local error message to the user.
Actionable Summary
To clean up your API layer, stop passing tokens manually. Create a scoped Axios instance, implement a request interceptor for headers, and a response interceptor for global error states like 401s. Always return the config or error object to keep the promise chain intact, and use separate instances for public vs. private API endpoints.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.