Prevent Axios Credential Leakage Across Routes
Managing authentication credentials across mixed public and private API endpoints introduces the risk of leaking sensitive tokens to unauthorized third parties or unauthenticated routes. When using Axios, credential leakage typically occurs due to globally shared default headers, improper request interceptor logic, or sending authenticated requests to external URLs. Implementing isolated client instances, route-specific interceptors, strict URL origin validation, and secure token storage prevents credentials from being exposed across application boundaries.
Separate Public and Authenticated Instances
The most effective pattern to avoid token leakage is separating the
HTTP client into dedicated instances rather than relying on the global
axios object or a single shared instance.
- Public Client: Configured without authentication headers or interceptors, used exclusively for unauthenticated endpoints such as user registration, login, and public asset retrieval.
- Authenticated Client: Configured with specific interceptors that attach bearer tokens, session tokens, or API keys strictly to protected internal routes.
import axios from 'axios';
// Public instance: No auth headers attached
export const publicApi = axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 10000,
});
// Authenticated instance: Configured for protected routes
export const privateApi = axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 10000,
});By decoupling these instances at the module level, developers
eliminate the possibility of accidentally inheriting global
Authorization headers on public calls.
Domain and Origin Whitelisting in Interceptors
When using interceptors on private instances, enforce strict domain
validation to ensure tokens are only appended to requests heading to
trusted first-party servers. This prevents scenarios where an
authenticated client makes a call to a relative path that redirects to
an external service (such as an S3 bucket or third-party webhook) while
carrying the Authorization header.
privateApi.interceptors.request.use((config) => {
const allowedOrigin = 'https://api.example.com';
const targetUrl = new URL(config.url, config.baseURL);
if (targetUrl.origin === allowedOrigin) {
const token = getAuthToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
} else {
// Strip or prevent adding the Authorization header for mismatched origins
delete config.headers.Authorization;
}
return config;
});Request-Level Metadata Flags
In architectures where a single instance is mandatory, use custom Axios request configuration flags to explicitly mark whether a request requires authentication.
// Request definition
export const fetchPublicContent = () => {
return apiClient.get('/content/public', {
requiresAuth: false,
});
};
// Interceptor evaluation
apiClient.interceptors.request.use((config) => {
if (config.requiresAuth !== false) {
const token = getAuthToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
});Explicitly checking metadata prevents the default assumption that every outgoing request should include credentials.
Isolation of External and Third-Party Calls
File uploads, analytics pings, and payment gateway interactions often require direct communication with third-party domains. Never reuse internal Axios instances for external endpoints. Instead, use an isolated, minimal instance:
export const externalUploadClient = axios.create({
// Clean configuration free from custom interceptors or default auth headers
timeout: 30000,
});Secure Credential Transport with HttpOnly Cookies
Whenever feasible, avoid manual header injection in JavaScript
altogether by relying on HttpOnly, Secure, and
SameSite cookies for authentication. Setting
withCredentials: true on the private Axios instance allows
the browser to handle token transmission automatically. When combined
with strict CORS policies and SameSite=Lax or
SameSite=Strict, the browser ensures credentials are never
accessible via JavaScript and are only sent to the matching origin
domain.