HTTP interceptors allow you to intercept and transform HTTP requests and responses globally in your Angular application. They’re useful for adding authentication tokens, logging, error handling, caching, and more.
Interface Definition
interface HttpInterceptor {
intercept (
req : HttpRequest < any >,
next : HttpHandler
) : Observable < HttpEvent < any >>;
}
Functional Interceptor
Angular also provides a functional approach with HttpInterceptorFn:
type HttpInterceptorFn = (
req : HttpRequest < unknown >,
next : HttpHandlerFn
) => Observable < HttpEvent < unknown >>;
Importing
import { HttpInterceptor , HttpRequest , HttpHandler } from '@angular/common/http' ;
import { Injectable } from '@angular/core' ;
@ Injectable ()
export class AuthInterceptor implements HttpInterceptor {
intercept ( req : HttpRequest < any >, next : HttpHandler ) {
// Interceptor logic
return next . handle ( req );
}
}
Methods
intercept()
Intercepts an HTTP request and handles it.
The outgoing request object to handle
next
HttpHandler | HttpHandlerFn
required
The next interceptor in the chain, or the backend if no interceptors remain
return
Observable<HttpEvent<any>>
An observable of the HTTP event stream
Usage Examples
Functional Interceptor (Recommended)
The functional approach is more lightweight and works with Angular’s injection context:
Authentication Token
Logging
Error Handling
Retry Logic
Caching
import { HttpInterceptorFn } from '@angular/common/http' ;
import { inject } from '@angular/core' ;
import { AuthService } from './auth.service' ;
export const authInterceptor : HttpInterceptorFn = ( req , next ) => {
const authService = inject ( AuthService );
const token = authService . getToken ();
if ( token ) {
const cloned = req . clone ({
headers: req . headers . set ( 'Authorization' , `Bearer ${ token } ` )
});
return next ( cloned );
}
return next ( req );
};
Class-based Interceptor (Legacy)
The traditional class-based approach:
Authentication
Loading Indicator
import { Injectable } from '@angular/core' ;
import {
HttpInterceptor ,
HttpRequest ,
HttpHandler ,
HttpEvent
} from '@angular/common/http' ;
import { Observable } from 'rxjs' ;
@ Injectable ()
export class AuthInterceptor implements HttpInterceptor {
constructor ( private authService : AuthService ) {}
intercept (
req : HttpRequest < any >,
next : HttpHandler
) : Observable < HttpEvent < any >> {
const token = this . authService . getToken ();
if ( token ) {
const cloned = req . clone ({
headers: req . headers . set ( 'Authorization' , `Bearer ${ token } ` )
});
return next . handle ( cloned );
}
return next . handle ( req );
}
}
Providing Interceptors
Functional Interceptors
Use withInterceptors() to provide functional interceptors:
import { bootstrapApplication } from '@angular/platform-browser' ;
import { provideHttpClient , withInterceptors } from '@angular/common/http' ;
import { authInterceptor } from './auth.interceptor' ;
import { loggingInterceptor } from './logging.interceptor' ;
bootstrapApplication ( AppComponent , {
providers: [
provideHttpClient (
withInterceptors ([ authInterceptor , loggingInterceptor ])
)
]
});
Class-based Interceptors
Use HTTP_INTERCEPTORS token to provide class-based interceptors:
import { HTTP_INTERCEPTORS } from '@angular/common/http' ;
import { AuthInterceptor } from './auth.interceptor' ;
@ NgModule ({
providers: [
{
provide: HTTP_INTERCEPTORS ,
useClass: AuthInterceptor ,
multi: true
}
]
})
export class AppModule {}
Or with standalone APIs:
import { provideHttpClient , withInterceptorsFromDi } from '@angular/common/http' ;
import { HTTP_INTERCEPTORS } from '@angular/common/http' ;
bootstrapApplication ( AppComponent , {
providers: [
provideHttpClient ( withInterceptorsFromDi ()),
{
provide: HTTP_INTERCEPTORS ,
useClass: AuthInterceptor ,
multi: true
}
]
});
Interceptor Chain
Interceptors are called in the order they are provided:
provideHttpClient (
withInterceptors ([
authInterceptor , // Called first
loggingInterceptor , // Called second
retryInterceptor , // Called third
cacheInterceptor // Called last
])
)
Each interceptor must call next() to pass the request to the next interceptor. If an interceptor doesn’t call next(), the request chain is broken and the request won’t reach the server.
Requests are immutable, so you must clone them to make changes:
export const modifyHeadersInterceptor : HttpInterceptorFn = ( req , next ) => {
// Clone the request and modify headers
const modified = req . clone ({
headers: req . headers
. set ( 'X-Custom-Header' , 'value' )
. set ( 'Accept' , 'application/json' )
});
return next ( modified );
};
Add Headers
Modify URL
Add Parameters
Set Credentials
Modify Body
const cloned = req . clone ({
headers: req . headers . set ( 'X-API-Key' , 'abc123' )
});
Transform responses using RxJS operators:
import { map } from 'rxjs/operators' ;
import { HttpResponse } from '@angular/common/http' ;
export const transformResponseInterceptor : HttpInterceptorFn = ( req , next ) => {
return next ( req ). pipe (
map ( event => {
if ( event instanceof HttpResponse ) {
// Transform the response body
return event . clone ({
body: {
data: event . body ,
timestamp: Date . now ()
}
});
}
return event ;
})
);
};
Conditional Interception
Apply interception logic conditionally:
export const conditionalInterceptor : HttpInterceptorFn = ( req , next ) => {
// Only intercept API calls
if ( req . url . startsWith ( '/api/' )) {
const modified = req . clone ({
headers: req . headers . set ( 'X-API-Key' , 'abc123' )
});
return next ( modified );
}
// Pass through without modification
return next ( req );
};
Error Handling
Handle errors at the interceptor level:
import { catchError } from 'rxjs/operators' ;
import { throwError } from 'rxjs' ;
import { Router } from '@angular/router' ;
import { inject } from '@angular/core' ;
export const authErrorInterceptor : HttpInterceptorFn = ( req , next ) => {
const router = inject ( Router );
return next ( req ). pipe (
catchError ( error => {
if ( error . status === 401 ) {
// Redirect to login on unauthorized
router . navigate ([ '/login' ]);
}
return throwError (() => error );
})
);
};
Best Practices
Use Functional Interceptors
Prefer functional interceptors (HttpInterceptorFn) over class-based interceptors for better tree-shaking and simpler code.
Keep Interceptors Focused
Each interceptor should have a single responsibility. Create multiple interceptors rather than one complex interceptor.
Always call next() (functional) or next.handle() (class-based) unless you intentionally want to block the request.
Requests are immutable. Always clone the request before modifying it.
Use proper error handling to prevent the interceptor chain from breaking.
Interceptors execute in the order they’re provided. Order matters for operations like authentication and logging.
Common Use Cases
Authentication Add authentication tokens to outgoing requests
Logging Log all HTTP requests and responses
Error Handling Centralized error handling and recovery
Loading States Show/hide loading indicators
Caching Cache responses for improved performance
Retry Logic Automatically retry failed requests
Request Transformation Modify requests before sending
Response Transformation Transform responses before consumption
See Also
HTTP Overview Introduction to Angular’s HTTP client
HttpClient Complete API reference for HttpClient