Interceptors let you modify the behavior of any dispatcher without changing your application code. They wrap the underlyingDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/nodejs/undici/llms.txt
Use this file to discover all available pages before exploring further.
dispatch function, giving you a hook to run logic before a request is sent, after a response is received, or both. undici ships eight built-in interceptors covering caching, retries, redirects, DNS caching, body decompression, response dumping, and request deduplication.
How interceptors work
An interceptor is a higher-order function with the signature:interceptor signature
dispatch function and returns a new dispatch function. The new function can inspect and mutate opts, wrap handler, or skip calling the underlying dispatch entirely.
The Dispatcher.compose() method chains interceptors by iterating over them and wrapping dispatch one level at a time:
compose() implementation (from lib/dispatcher/dispatcher.js)
request, stream, close, etc.) continue to work on the original dispatcher while dispatch is replaced with the composed version.
Interceptor execution order
When you compose multiple interceptors, the last one in the array runs first on incoming requests (and last on outgoing responses). This is a consequence of function composition.interceptor order visualization
Composing multiple interceptors
Pass interceptors as an array or as individual arguments to.compose():
composing interceptors on an Agent
Client and Pool:
composing on a Client
Built-in interceptors
interceptors.cache
Implements client-side HTTP caching following RFC 9111. Checks the cache store before dispatching a request; stores cacheable responses after receiving them.
The backing store for cached responses. undici ships
MemoryCacheStore and SqliteCacheStore.HTTP methods whose responses are eligible for caching. Must be safe methods per RFC 9110.
Cache type.
private caches responses with Cache-Control: private, which may contain user-specific data.Default expiry in seconds for responses that have no explicit expiration and no heuristic expiry. Undefined by default (such responses are not cached).
Allowlist of origins to cache. If omitted, all origins are cached.
- In-memory cache
- SQLite cache
cache interceptor with MemoryCacheStore
If-Modified-Since, If-None-Match), stale-while-revalidate (background revalidation), stale-if-error thresholds, and the Age response header. Requests with Cache-Control: no-store bypass the cache entirely.
interceptors.retry
Automatically retries failed requests using the RetryHandler. Supports exponential backoff, configurable status codes, and per-request overrides.
Maximum number of retry attempts.
Minimum delay in milliseconds before the first retry.
Maximum delay in milliseconds between retries.
Multiplier applied to the delay on each successive retry (exponential backoff).
When the server returns a
Retry-After header, honor it as the delay.HTTP status codes that trigger a retry.
HTTP methods eligible for retry. Non-idempotent methods (
POST, PATCH) are excluded by default.retry interceptor with backoff
opts.retryOptions, which are merged with the interceptor-level defaults.
interceptors.redirect
Follows HTTP redirects automatically. Supports 301, 302, 303, 307, and 308 status codes.
Maximum number of redirects to follow. Requests without this option set (or with
0) skip the interceptor entirely.When
true, throw a MaxRedirectsError if the redirect limit is reached instead of returning the final redirect response.redirect interceptor
maxRedirections can also be set per-request in opts.maxRedirections, which overrides the interceptor default.
interceptors.dns
Caches DNS lookups per origin for a configurable TTL, avoiding repeated DNS resolution for the same hostname. Supports dual-stack (IPv4 + IPv6) with happy-eyeballs-like fallback and pluggable storage.
Maximum cache entry lifetime in milliseconds. Set to
0 to disable TTL (cache indefinitely).Maximum number of hostnames to cache simultaneously.
Resolve both IPv4 and IPv6 addresses. Enables automatic fallback to the other address family on connection failure.
Preferred address family when
dualStack is false.Custom DNS lookup function. Defaults to Node.js
dns.lookup. Must follow the signature (hostname, options, callback).Custom record selection function. Defaults to simplified round-robin. Receives
(origin, records, affinity) and returns a single record.Custom storage backend for DNS records. Must implement
{ get, set, delete, full, size }.- Basic usage
- LRU cache storage
dns interceptor with default options
dualStack is enabled, the DNS interceptor uses a happy-eyeballs-like algorithm: if a connection to the resolved address fails with ETIMEDOUT or ECONNREFUSED, it automatically tries the other address family before propagating the error.
interceptors.dump
Discards response bodies up to a configurable size limit. Useful when you want to consume the response to free the connection, but do not need the body content. Prevents unbounded memory use by closing the connection if the body exceeds maxSize.
Maximum bytes to read and discard before considering the body dumped. If the
content-length response header exceeds this value, the connection is closed immediately. Default: 1 MB.dump interceptor
dumpMaxSize option can also be provided per-request in opts.dumpMaxSize, overriding the interceptor-level default:
per-request dump size override
interceptors.decompress
Automatically decompresses response bodies encoded with gzip, deflate, brotli, or zstd. Removes content-encoding and content-length headers from decompressed responses and supports multiple chained encodings per RFC 9110.
Skip decompression for responses with status codes >= 400.
Status codes for which decompression is skipped entirely.
| Encoding | Algorithm |
|---|---|
gzip, x-gzip | GZIP (via createGunzip) |
deflate, compress, x-compress | DEFLATE (via createInflate) |
br | Brotli (via createBrotliDecompress) |
zstd | Zstandard (via createZstdDecompress) |
decompress interceptor
decompress with custom options
interceptors.deduplicate
Deduplicates concurrent in-flight requests with identical parameters. When multiple requests arrive for the same origin, method, path, and headers, only the first is dispatched to the network. All subsequent identical requests wait for the first to complete and then receive the same response.
HTTP methods eligible for deduplication. Must be safe methods only.
Header names whose presence causes a request to bypass deduplication entirely. Case-insensitive. Useful for
Idempotency-Key.Header names excluded from the deduplication key. Requests with different values for these headers are still deduplicated together. Useful for per-request headers like
X-Request-Id.Maximum bytes buffered per waiting deduplicated handler. If exceeded, the handler fails with an abort error. Default: 5 MB.
deduplicate interceptor
excludeHeaderNames). Deduplication events are published to the undici:request:pending-requests diagnostics channel for observability.
skip deduplication for idempotency-key requests
Combining interceptors: a complete example
The following example wires up DNS caching, HTTP caching, automatic retries, and redirect following on a singleAgent and sets it as the global dispatcher:
production-ready Agent with multiple interceptors
dns → cache → retry → redirect → decompress → dispatcher. The response flows back in reverse, so decompress runs on the raw compressed bytes before the cache stores the decompressed result.