The ProblemEverything works fine when your proxy_pass points to a static URL. However, the moment you introduce a variable to make your upstream dynamic, Nginx might throw a tantrum. You’ll notice your site returning 502 errors, and your logs will be filled with complaints about a missing resolver.
Check your error logs at /var/log/nginx/error.log. You will likely see an entry like this:
2023/10/27 10:45:01 [error] 12345#0: *1 no resolver defined to resolve "api.example.com", client: 1.2.3.4, server: example.com, request: "GET / HTTP/1.1", upstream: "http://api.example.com/"
A Typical Failing ConfigurationThis issue usually crops up in blocks that look like this:
location / {
set $backend_host "api.example.com";
proxy_pass http://$backend_host;
}
Why Nginx Fails at RuntimeNginx handles DNS resolution differently depending on how you write your proxy_pass directive.
- Static Resolution: When you use
proxy_pass http://api.example.com;, Nginx resolves the domain name exactly once during startup or reload. It caches that IP address forever until the next reload.- Dynamic Resolution: Using a variable likeproxy_pass http://$variable;changes the rules. Because the variable could change for every single request, Nginx must resolve the domain at runtime.Here is the catch: Nginx does not automatically use your system's/etc/resolv.conffor these runtime lookups. It requires you to explicitly define a DNS server using theresolverdirective. Without it, Nginx has no way to translate that domain string into an IP address.
The Fix: Adding the Resolver DirectiveTo fix this, you must tell Nginx which DNS server to trust. You can place this directive in your http, server, or location block.
Option 1: Public DNS ServersIf your server needs to reach public endpoints, Google or Cloudflare are reliable choices. Add this to your server block:
server {
listen 80;
server_name example.com;
# Use Google and Cloudflare DNS
resolver 8.8.8.8 1.1.1.1 valid=30s;
location / {
set $upstream_endpoint "api.example.com";
proxy_pass http://$upstream_endpoint;
}
}
Option 2: Internal/Cloud DNSIn a cloud environment, you should use the local VPC resolver. On AWS, this is always the base of your VPC network range plus two. For a 10.0.0.0/16 network, your resolver is 10.0.0.2. On modern Ubuntu systems using systemd-resolved, use the local stub:
# For local systemd-resolved
resolver 127.0.0.53 valid=10s;
# For AWS VPC (example)
resolver 10.0.0.2 valid=10s;
Option 3: Disabling IPv6If your network doesn't support IPv6, Nginx might waste time trying to resolve AAAA records. You can force it to stick to IPv4 to speed things up:
resolver 8.8.8.8 ipv6=off;
Applying the ChangesNever restart Nginx without testing the config first. A small typo can take your whole site down.
- Validate Syntax:
sudo nginx -tLook forsyntax is okin the output.- Reload Gracefully:sudo systemctl reload nginx- **Monitor the Result:**Watch the logs in real-time as you hit your application:tail -f /var/log/nginx/error.log## Expert Tips- Mind the TTL: Dynamic backends like AWS Load Balancers change IPs frequently. Setvalid=10sor30sto ensure Nginx doesn't hold onto stale IP addresses.- Redundancy: Always list at least two DNS servers. If one goes down, your proxy won't break.- Global Scope: If you use dynamic variables across many sites, put theresolverin yournginx.confhttp block to avoid repeating yourself.

