Down the rabbit holeEngineering·10 min read

HTTP Interceptor vs Proxy Interceptor: Angular, Axios and Fetch Compared

Grab coffee. This goes deep.

Code interceptors belong to your app. Proxy interceptors belong to your debugging session. Mixing them up is how mock data ends up in production.

The short version

  • Put permanent behaviour (auth headers, token refresh, logging, error mapping) in code interceptors.
  • Put temporary scenarios (mocks, failures, delays, redirects) in a proxy interceptor, outside the codebase.
  • Never ship a mock interceptor behind a feature flag.

Angular HttpInterceptor

Modern Angular uses functional interceptors registered with provideHttpClient.

auth.interceptor.ts
JS
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).token();
return next(token ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : req);
};
// app.config.ts
provideHttpClient(withInterceptors([authInterceptor]));

Axios interceptors

Refresh on 401, retry once
CODE
api.interceptors.response.use(undefined, async (error) => {
const original = error.config;
if (error.response?.status === 401 && !original._retried) {
original._retried = true;
await refreshSession();
return api(original);
}
return Promise.reject(error);
});

Fetch has no interceptor API

The Fetch API has no built-in hooks, so teams write a wrapper such as apiFetch() and use it everywhere. Wrapping window.fetch globally is possible, and it is exactly what browser interceptor tools do internally, but it is fragile in application code.

Where a proxy interceptor fits

Now suppose you need to see how the UI above behaves when the refresh endpoint returns 500, or when /me takes 8 seconds. You *could* add a mock interceptor, but then you have to commit, build and remember to remove it. A proxy interceptor makes the same change from outside the code, only in your browser, and your real interceptors still run so you are testing the true behaviour.

ProxyCeptor rule: make token refresh fail
JSON
{
"name": "Refresh endpoint fails",
"match": { "urlPattern": "*/auth/refresh", "matchType": "wildcard" },
"response": {
"body": { "enabled": true, "mode": "replace-whole", "statusCode": 500, "value": "{}" }
}
}

Decision table

TaskWhere it belongs
Attach auth headerCode interceptor
Refresh expired tokenCode interceptor
Global error toastCode interceptor
Test what happens when refresh failsProxy interceptor
Demo a feature before the API existsProxy interceptor
Reproduce a customer's exact payloadProxy interceptor
Unit testsTest-level mocks (MSW, HttpTestingController, Jest)

Frequently asked questions

Can a proxy interceptor see requests made by Angular HttpClient?

Yes. HttpClient uses XHR (or fetch with withFetch()), and ProxyCeptor intercepts both in the page.

Do proxy interceptor rules run before or after my Axios interceptors?

After your request interceptors (the browser sees the final request) and before your response interceptors (your code receives the modified response). That means you are testing your real interceptor logic.

Is MSW a proxy interceptor?

Mock Service Worker intercepts at the service-worker level and is configured in code, so it sits between the two: ideal for tests, less convenient for ad-hoc debugging. See ProxyCeptor vs MSW.

☕
Written by

Kartikay Tiwari Consultant

Kartikay is a consultant who helps teams with API-heavy web and streaming apps, from architecture reviews to the last mile of QA. His posts are the calm, thorough ones: a long explanation, a real playbook and a single sentence that ends the argument. Expect measured opinions, war stories with the details changed and a strong view on Smart TV surprises.

“That will be one coffee, invoice to follow.”

More from Kartikay →

Read next

Intercept your first request in under a minute

Create a free ProxyCeptor account to mock, delay, block and rewrite API traffic, then share the same rules with your team.

🚀 Mock, delay and break API calls in ChromeTry ProxyCeptor free