NGINX DNS Resolver Security

Configure the resolver directive securely to prevent DNS poisoning attacks that can redirect your traffic to malicious servers.

Updated: January 2025 * 7 min read

Why Resolver Configuration Matters

The NGINX resolver directive specifies DNS servers for resolving hostnames in proxy_pass, upstream, and other directives. Using external or untrusted DNS servers can allow attackers to redirect your traffic.

DNS Poisoning Attack

An attacker who can poison DNS responses can:

  • Redirect proxy traffic - Send requests to malicious servers
  • Steal credentials - Intercept authentication requests
  • Inject malicious content - Modify responses to users
  • Access internal services - DNS rebinding attacks

When NGINX Needs a Resolver

NGINX requires a resolver when hostnames need to be resolved at runtime:

  • Dynamic proxy_pass - Using variables in the destination
  • OCSP Stapling - Fetching certificate status
  • Upstream with variables - Dynamic backend selection
  • SSL certificate verification - Resolving OCSP responders
# These require a resolver:
proxy_pass http://$backend;
proxy_pass https://api.$region.example.com;

# This does NOT need a resolver (resolved at startup):
proxy_pass http://static-backend.example.com;

Vulnerable Patterns

Using Public DNS Servers

# External DNS servers can be poisoned
resolver 8.8.8.8 8.8.4.4;

location / {
    proxy_pass http://$backend_host;
}

# An attacker could poison 8.8.8.8's response
# to redirect $backend_host to their server

Using ISP DNS

# ISP DNS is often insecure and can be intercepted
resolver 192.168.1.1;

# Many ISPs don't implement DNSSEC
# Man-in-the-middle attacks are easier

No Validation

# Missing valid parameter allows stale/poisoned records
resolver 8.8.8.8;

# Without valid=Xs, cached records may persist too long

Secure Configuration

Use Local/Internal DNS

# Use your internal DNS infrastructure
resolver 127.0.0.1 valid=30s;

# Or your organization's internal DNS servers
resolver 10.0.0.53 10.0.0.54 valid=30s;

Use systemd-resolved (Local)

# systemd-resolved provides local caching
resolver 127.0.0.53 valid=30s;

# This uses the system's configured DNS with validation

Use Kubernetes DNS (In Clusters)

# In Kubernetes, use the cluster DNS
resolver kube-dns.kube-system.svc.cluster.local valid=30s;

# Or the CoreDNS service IP
resolver 10.96.0.10 valid=30s;

Configure Validation

# Always set valid parameter
resolver 127.0.0.1 valid=30s ipv6=off;

# valid=30s - Re-resolve every 30 seconds
# ipv6=off - Disable if not using IPv6 (reduces attack surface)

Best Practices

1. Avoid Dynamic Resolution When Possible

# Instead of dynamic resolution
# BAD:
set $backend "api.internal.example.com";
proxy_pass http://$backend;

# GOOD: Use upstream blocks (resolved at startup)
upstream api_backend {
    server api.internal.example.com:80;
}

location / {
    proxy_pass http://api_backend;
}

2. Use IP Addresses for Critical Backends

# Direct IP avoids DNS entirely
upstream auth_backend {
    server 10.0.1.100:8080;
    server 10.0.1.101:8080;
}

# No DNS resolution needed = no DNS attacks possible

3. Short TTL for Dynamic Backends

# Short valid time reduces poisoning window
resolver 127.0.0.1 valid=10s;

# Balance between security and performance
# Too short = high DNS load
# Too long = stale records / longer poisoning window

4. Use Multiple Resolvers

# Multiple resolvers provide redundancy
resolver 10.0.0.53 10.0.0.54 valid=30s;

# NGINX will round-robin between them
Cloud Environments: In AWS, use the VPC DNS (169.254.169.253). In GCP, use the metadata server DNS. These are trusted internal resolvers.

OCSP Stapling Resolver

OCSP stapling requires a resolver to fetch certificate status. This is a common place where external resolvers get configured.

# Safe OCSP configuration
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/chain.pem;

# Use internal resolver for OCSP
resolver 127.0.0.1 valid=300s;
resolver_timeout 5s;

Detecting with Gixy

Gixy detects external resolver usage:

$ gixy /etc/nginx/nginx.conf

==================== Results ====================

[resolver_external] Do not use external nameservers for "resolver".
  Severity: HIGH
  Description: External DNS servers allow DNS poisoning attacks
               to redirect traffic to malicious servers.
  Reason: Using external resolver "8.8.8.8".
  Pseudo config:
      http {
          resolver 8.8.8.8;
      }
  File: /etc/nginx/nginx.conf
  Line: 15

==================== Summary ====================
Total issues: 1 (High: 1)

Static vs Dynamic Resolution

Configuration Resolution Time Needs Resolver?
proxy_pass http://backend.local; Startup No
proxy_pass http://10.0.0.1; Never No
upstream { server backend.local; } Startup No
proxy_pass http://$variable; Runtime Yes
ssl_stapling on; Runtime Yes

Security Checklist

  • Never use public DNS (8.8.8.8, 1.1.1.1) for resolver
  • Use local or internal DNS servers
  • Always set the valid parameter
  • Prefer static hostnames or IPs over dynamic resolution
  • Use upstream blocks instead of variables when possible
  • Consider ipv6=off if not using IPv6
  • Run Gixy to detect external resolver usage

Further Reading