Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Angular Dictionary
  3. HTTP Interceptor

HTTP Interceptor

Since: Angular 14(2022)

An HTTP interceptor in Angular intercepts outgoing requests and incoming responses globally, allowing you to add authorization headers, handle errors centrally, or log traffic without modifying individual service classes. Angular 15+ introduced a function-based interceptor (HttpInterceptorFn) in addition to the traditional class-based approach (HttpInterceptor). Function-based interceptors are the default style since Angular 15.

Function-based Interceptor (Angular 15+)

// auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const token = localStorage.getItem('token');
  const authReq = token
    ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
    : req;
  return next(authReq);
};
// app.config.ts
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])),
  ],
};

Class-based Interceptor

// auth.interceptor.ts (class-based)
import { Injectable } from '@angular/core';
import {
  HttpInterceptor, HttpRequest, HttpHandler, HttpEvent
} from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');
    const authReq = token
      ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
      : req;
    return next.handle(authReq);
  }
}

Sample Code

An error handling interceptor that converts HTTP errors into user-friendly messages.

// error-handling.interceptor.ts
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';

export const errorHandlingInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      let message = 'An unexpected error occurred.';
      if (error.status === 401) {
        message = 'Authentication required. Please log in.';
      } else if (error.status === 403) {
        message = 'Access denied.';
      } else if (error.status === 404) {
        message = 'Resource not found.';
      } else if (error.status >= 500) {
        message = 'Server error. Please try again later.';
      }
      console.error(message, error);
      return throwError(() => new Error(message));
    })
  );
};

A logging interceptor that records request timing.

// logging.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { tap } from 'rxjs';

export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
  const start = Date.now();
  console.log(`[HTTP] ${req.method} ${req.url}`);
  return next(req).pipe(
    tap({
      next:  () => console.log(`[HTTP] ${req.method} ${req.url} — ${Date.now() - start}ms`),
      error: (err) => console.error(`[HTTP] Error: ${err.message}`),
    })
  );
};

Registering multiple interceptors. They run in the order they are listed.

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authInterceptor }          from './auth.interceptor';
import { loggingInterceptor }       from './logging.interceptor';
import { errorHandlingInterceptor } from './error-handling.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withInterceptors([
        loggingInterceptor,        // 1st: log the request
        authInterceptor,           // 2nd: add auth header
        errorHandlingInterceptor,  // 3rd: handle errors
      ])
    ),
  ],
};

Notes

ItemDescription
Request immutabilityHttpRequest objects are immutable. Use req.clone({ ... }) to create a modified copy before passing it to next.
Must call next()Every interceptor must call next(req) (function-based) or next.handle(req) (class-based) to pass the request along the chain. Omitting it silently swallows the request.
Interceptor orderInterceptors run in the order they are listed in the withInterceptors array for requests, and in reverse order for responses.
Skipping an interceptorTo skip an interceptor for specific requests, check req.url or use a custom context (HttpContext) inside the interceptor.

Summary

HTTP interceptors provide a centralized place to add cross-cutting concerns to all HTTP requests and responses: authentication headers, error handling, logging, caching, and retry logic. Function-based interceptors (HttpInterceptorFn) were introduced in Angular 15 and are used with the Standalone API; class-based interceptors are still supported for NgModule projects.

Always clone the request with req.clone() before modifying it, and always call next() to continue the chain. For making HTTP requests in services, see HttpClient.

Common Mistake: Mutating the request object instead of cloning it

HttpRequest is immutable. You cannot set headers or modify properties directly. Always use req.clone({ ... }) to produce a modified copy.

// NG: mutating the request directly — the change is silently ignored
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  req.headers.set('Authorization', 'Bearer token'); // has no effect — HttpHeaders is immutable
  return next(req);
};
// OK: use req.clone() to create a modified copy
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const authReq = req.clone({ setHeaders: { Authorization: 'Bearer token' } });
  return next(authReq);
};

Common Mistake: Forgetting to call next() — request never sent

If an interceptor does not call next(req), the request chain is broken and no HTTP request is sent. The component will wait forever for a response.

// NG: next() not called — request is swallowed
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
  console.log(req.url);
  // return next(req); // missing — request never sent
  return EMPTY;
};
// OK: always return next(req)
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
  console.log(req.url);
  return next(req);
};

Common Mistake: Causing an infinite loop by making a request inside an interceptor without a skip condition

Making an HTTP request inside an interceptor (e.g., to refresh a token) triggers the same interceptor again, causing an infinite loop. Guard against it by checking the request URL or using HttpContext.

// NG: refreshing token inside the interceptor re-triggers itself
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  return next(req).pipe(
    catchError(err => {
      if (err.status === 401) {
        return http.post('/auth/refresh', {}).pipe(...); // triggers authInterceptor again
      }
      return throwError(() => err);
    })
  );
};
// OK: skip the interceptor for the refresh endpoint
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  if (req.url.includes('/auth/refresh')) {
    return next(req); // skip — no header, no retry
  }
  // normal interception logic
  return next(req);
};

If you find any errors or copyright issues, please .