← Back to Articles         
Why do we need HTTP Interceptors?

Why do we need HTTP Interceptors?

Published on 01 Oct 2026     6 min read Angular
HTTP Interceptors

1. Why do we need HTTP Interceptors?

What is the need?

HTTP interceptors are used to handle common HTTP request/response logic in one central place instead of repeating the same code in every Angular service.

For example, suppose your application has:

ProductService
CustomerService
OrderService
PaymentService
ArticleService

All these services call your ASP.NET Core Web API, and every secured request needs:

Authorization: Bearer <JWT_TOKEN>

Without an interceptor, you would have to manually attach the JWT token in every service.

Without Interceptor

getProducts() {

  const token = localStorage.getItem('token');

  const headers = {
    Authorization: `Bearer ${token}`
  };

  return this.http.get(
    'https://localhost:7001/api/products',
    { headers }
  );
}

Then you repeat the same thing for orders:

getOrders() {

  const token = localStorage.getItem('token');

  const headers = {
    Authorization: `Bearer ${token}`
  };

  return this.http.get(
    'https://localhost:7001/api/orders',
    { headers }
  );
}

And again for customers, payments, articles, etc.

That creates unnecessary duplication.


With Interceptor

Your services remain simple:

getProducts() {
  return this.http.get(
    'https://localhost:7001/api/products'
  );
}
getOrders() {
  return this.http.get(
    'https://localhost:7001/api/orders'
  );
}

The interceptor automatically adds:

Authorization: Bearer <JWT_TOKEN>

to applicable requests.

Flow

ProductService
      │
      │ HttpClient.get()
      ▼
┌─────────────────────────┐
│   Auth Interceptor      │
│                         │
│ Get JWT token           │
│ Add Authorization       │
│ header                  │
└────────────┬────────────┘
             │
             │ Authorization:
             │ Bearer eyJhbG...
             ▼
┌─────────────────────────┐
│ ASP.NET Core Web API    │
└─────────────────────────┘

Common uses of interceptors

Interceptors are useful for cross-cutting concerns such as:

Requirement Interceptor responsibility
JWT authentication Add Authorization header
Error handling Handle HTTP errors centrally
Logging Log requests/responses
Correlation ID Add correlation ID header
Loading indicator Show/hide spinner
Token refresh Handle expired access tokens
Custom headers Add application-wide headers
API timing Measure request duration

Main advantage

Instead of:

Service A → JWT logic
Service B → JWT logic
Service C → JWT logic
Service D → JWT logic

we have:

Service A ─┐
Service B ─┤
Service C ─┼──→ Interceptor → Web API
Service D ─┘

This follows the DRY (Don't Repeat Yourself) principle.


2. How do you attach a JWT token to every API request?

For a modern standalone Angular application, you can create a functional interceptor.

Full Simple Example

Assume the JWT token is stored after login.

auth.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  getToken(): string | null {
    return localStorage.getItem('accessToken');
  }

  setToken(token: string): void {
    localStorage.setItem('accessToken', token);
  }

  removeToken(): void {
    localStorage.removeItem('accessToken');
  }
}

auth.interceptor.ts

import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
import { AuthService } from './auth.service';

export const authInterceptor: HttpInterceptorFn = (req, next) => {

  const authService = inject(AuthService);

  const token = authService.getToken();

  if (token) {

    const authRequest = req.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });

    return next(authRequest);
  }

  return next(req);
};

app.config.ts

Register the interceptor:

import { ApplicationConfig } from '@angular/core';

import {
  provideHttpClient,
  withInterceptors
} from '@angular/common/http';

import { authInterceptor } from './auth.interceptor';

export const appConfig: ApplicationConfig = {

  providers: [

    provideHttpClient(
      withInterceptors([
        authInterceptor
      ])
    )

  ]

};

product.service.ts

Now the service doesn't need to know anything about the JWT token.

import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface Product {
  id: number;
  name: string;
  price: number;
}

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  private http = inject(HttpClient);

  private apiUrl =
    'https://localhost:7001/api/products';

  getProducts(): Observable<Product[]> {

    return this.http.get<Product[]>(
      this.apiUrl
    );
  }
}

Notice that we did not write:

Authorization: `Bearer ${token}`

inside ProductService.

The interceptor handles it automatically.


How does the JWT interceptor work?

Suppose login returns:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs..."
}

You save it:

localStorage.setItem(
  'accessToken',
  response.accessToken
);

Later:

this.http.get(
  'https://localhost:7001/api/products'
);

Angular passes the request through the interceptor.

Step 1 — Get token

const token = authService.getToken();

Result:

eyJhbGciOiJIUzI1NiIs...

Step 2 — Clone the request

const authRequest = req.clone({

Angular HTTP requests are immutable, so you don't modify the original request directly.

Step 3 — Add Authorization header

setHeaders: {
  Authorization: `Bearer ${token}`
}

The outgoing request becomes:

GET /api/products HTTP/1.1

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Step 4 — Continue the request

return next(authRequest);

next() passes the modified request to the next interceptor or ultimately to the server.


Complete Flow

User Login
    │
    ▼
ASP.NET Core Web API
    │
    │ returns JWT
    ▼
Angular
    │
    ▼
Store Access Token
    │
    │
    │ Later...
    ▼
ProductService
    │
    │ GET /api/products
    ▼
HttpClient
    │
    ▼
Auth Interceptor
    │
    ├── Get JWT
    │
    ├── Clone Request
    │
    └── Add Authorization Header
            │
            ▼
Authorization: Bearer <JWT>
            │
            ▼
ASP.NET Core Web API
            │
            ▼
JWT Authentication
            │
            ▼
ProductsController

Important: Should every request receive the JWT?

Not necessarily.

For example, you may have:

POST /api/account/login
GET  /api/products
GET  /api/orders

The login request obviously doesn't need the existing access token.

You can exclude certain endpoints:

export const authInterceptor: HttpInterceptorFn = (req, next) => {

  const authService = inject(AuthService);

  if (req.url.includes('/api/account/login')) {
    return next(req);
  }

  const token = authService.getToken();

  if (!token) {
    return next(req);
  }

  const authRequest = req.clone({
    setHeaders: {
      Authorization: `Bearer ${token}`
    }
  });

  return next(authRequest);
};

In a production application, it is also sensible to ensure you attach credentials only to API origins you trust.


Key Points

  • Interceptors provide centralized HTTP processing.
  • They avoid repeating JWT/header/error-handling code in every service.
  • JWT tokens are commonly attached through an authentication interceptor.
  • Modern Angular supports functional interceptors with:
HttpInterceptorFn
  • Register functional interceptors using:
provideHttpClient(
  withInterceptors([authInterceptor])
)
  • Read the token:
const token = authService.getToken();
  • Clone the request:
req.clone()
  • Add the JWT:
setHeaders: {
  Authorization: `Bearer ${token}`
}
  • Continue the pipeline:
next(authRequest)
  • The interceptor does not provide security by itself. The ASP.NET Core API must validate the JWT and perform authorization.

Interview Questions and Answers

Q1. Why do we need HTTP interceptors?

HTTP interceptors centralize common request and response processing such as authentication, logging, error handling, correlation IDs, and token refresh, avoiding duplicated code across services.

Q2. How do you attach a JWT token to every API request?

Create an HttpInterceptorFn, retrieve the token, clone the HTTP request, add the Authorization: Bearer <token> header, and register the interceptor using withInterceptors().

const authRequest = req.clone({
  setHeaders: {
    Authorization: `Bearer ${token}`
  }
});

return next(authRequest);

Q3. Why do we clone the HTTP request?

Because Angular HttpRequest objects are immutable. To change headers or other request properties, we create a modified copy using req.clone().

Q4. What does next(authRequest) mean?

It forwards the modified request to the next interceptor in the chain, or to the HTTP backend if there are no more interceptors.

Q5. Should JWT validation happen in Angular?

No. Angular sends the JWT, but the backend must perform the actual authentication and authorization.

Angular
   │
   │ JWT
   ▼
Interceptor
   │
   │ Authorization: Bearer <JWT>
   ▼
ASP.NET Core API
   │
   ├── Validate signature
   ├── Validate issuer
   ├── Validate audience
   ├── Validate expiration
   └── Apply authorization

Interview one-line answer: An Angular HTTP interceptor provides a centralized place to process HTTP requests and responses; for JWT authentication, it can automatically clone outgoing API requests and add the Authorization: Bearer <token> header before forwarding them to the backend.

← Back to Articles