Open-Source Wikis

/

nginx

/

Systems

/

Resolver

nginx/nginx

Resolver

Active contributors: Maxim Dounin, Sergey Kandaurov, Roman Arutyunyan

Purpose

src/core/ngx_resolver.{c,h} is nginx's hand-rolled async DNS client. It exists because most of nginx's hot path runs inside a single-threaded event loop where getaddrinfo() is unacceptable — a blocking call freezes the whole worker. The resolver issues UDP DNS queries (with TCP fallback for truncated responses), caches answers, supports A / AAAA / SRV / PTR record types, and integrates with the rest of nginx through the standard event interface.

ngx_resolver.c is ~3,100 lines — large for src/core/, but justified by a feature surface that includes timeouts, retries across multiple resolvers, and a per-cycle cache.

Files

File Role
src/core/ngx_resolver.{c,h} The async resolver

Key abstractions

Type Role
ngx_resolver_t Per-cycle state — UDP/TCP sockets, cache rbtrees, query queue
ngx_resolver_ctx_t A pending resolve request — caller's callback, name, type
ngx_resolver_node_t A cached entry (name + records + TTL deadline)
ngx_resolve_create() Build a resolver from a resolver directive
ngx_resolve_start() Allocate an ngx_resolver_ctx_t for a name
ngx_resolve_name() Kick off resolution — calls back when answers arrive
ngx_resolve_addr() Reverse PTR resolution
ngx_resolve_name_done() Cancel + release a context

How a resolution flows

sequenceDiagram
    participant Caller
    participant R as ngx_resolver_t
    participant Cache as Cache rbtree
    participant Net as UDP/TCP socket
    participant DNS as Upstream DNS

    Caller->>R: ngx_resolve_name(ctx, "example.com")
    R->>Cache: lookup
    alt cache hit (fresh)
        Cache-->>R: addrs
        R->>Caller: ctx->handler(ctx, addrs) (next event loop tick)
    else cache miss / stale
        R->>Net: send DNS query packet
        R->>R: arm timer for retry
        Net->>DNS: A/AAAA query
        DNS-->>Net: answer
        Net->>R: parse, validate
        R->>Cache: insert with TTL
        R->>Caller: ctx->handler(ctx, addrs)
    end

The caller provides:

  • The hostname (or IP for reverse).
  • An ngx_resolver_ctx_t.handler callback.
  • An ngx_resolver_ctx_t.timeout in milliseconds.
  • An ngx_resolver_ctx_t.data pointer for context.

When the answer arrives (or the timeout expires), the handler runs from the resolver's event handler. The caller's stack frame is gone by then; the resolver context is what carries forward the work.

Cache

Two rbtrees (per cycle):

  • Name cache — keys by hostname, holds A and AAAA addrs.
  • Address cache — keys by IP, holds reverse PTRs.

Each entry has a TTL deadline (expire) drawn from the DNS response's TTL. ngx_resolver_expire() runs from a timer to evict stale entries.

When a query is in flight, its node sits in a "waiting" state with a queue of ngx_resolver_ctx_t callers. Subsequent requests for the same name that arrive while the query is pending join the queue rather than firing a duplicate query (request collapsing).

Retries and multiple servers

resolver 8.8.8.8 1.1.1.1 valid=30s ipv6=off; configures multiple upstream resolvers. Queries round-robin across them; on timeout, the next server is tried. The valid= parameter overrides the DNS TTL (for cases where you want to cache longer than the upstream allows).

ipv6=off skips AAAA lookups entirely. ipv4=off skips A. When both A and AAAA are requested (the default), the resolver fires both queries in parallel and the callback fires when both complete (or times out).

Who uses it

  • HTTP proxy_pass http://hostname; with a literal hostname.
  • HTTP resolver directive at server scope plus dynamic upstream names.
  • Stream proxy_pass hostname:port;.
  • Mail auth_http http://hostname/.
  • OCSP stapling to fetch responses from the OCSP responder URL.

If proxy_pass http://hostname; is configured without a resolver directive in scope, hostnames are resolved once at config time (essentially a synchronous gethostbyname during reload). Adding resolver makes resolutions happen at request time.

Integration points

  • Event loop — the resolver opens UDP and TCP sockets and registers them as connections. Read events drive query parsing.
  • Cycle / configuration — the resolver directive instantiates an ngx_resolver_t, attached to the per-context config.
  • Timers — query timeouts and cache expiry both ride on the standard timer rbtree.

Entry points for modification

Most changes here are bug fixes around edge cases (CNAME chains, DNAME, malformed answers). The DNS wire-format parser is hand-rolled and conservative — adding new record types means extending both the parser and a new variant of ngx_resolver_ctx_t. DoH (DNS over HTTPS) is not supported in mainline; the resolver speaks only RFC 1034/1035 over UDP and TCP.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Resolver – nginx wiki | Factory