nginx duplicate server block for same server_name
Duplicate server blocks with the same server_name cause nginx to serve the first matching block and ignore later ones, often without obvious errors. This guide shows how to trace the active configuration, fix the conflict, and prevent it on Linux and Windows.
Rao Aadil, India 7 min read
Confirm the duplicate server block is the problem
When you edit a server block for example.com and the live site still serves old content, check for a duplicate server_name first. Nginx merges all config files into one effective configuration. Two blocks with the same server_name on the same listen address and port trigger a warning, and nginx uses whichever one it loads first. The other block is ignored entirely. Edits to that file do nothing.
Print the entire effective configuration and search for server_name. On Linux, run:
nginx -T | grep -n "server_name"
On Windows PowerShell, the equivalent is:
nginx -T | Select-String "server_name"
From cmd.exe, use:
nginx -T | findstr /i "server_name"
nginx -T dumps the complete merged configuration, including the file path of each included file. Look for lines like server_name example.com; appearing more than once, and note any file boundary comments such as # configuration file /etc/nginx/sites-enabled/example.com.conf:.
The warning to look for:
nginx: [warn] conflicting server name "example.com" on 0.0.0.0:80, ignored
You'll see this when two blocks share a server_name and listen address. It appears during a reload, a start, or a config test. On Windows, check the same output from an elevated command prompt; the warning goes to stderr and the error log at C:\nginx\logs\error.log.
Confirm which block actually serves the request by sending an explicit Host header. On Linux:
curl -H "Host: example.com" http://localhost
On Windows PowerShell:
Invoke-WebRequest -Headers @{Host="example.com"} http://localhost
Compare the response body, headers, or upstream behavior with what you expect from the block you edited. If the response comes from the old block, you have a duplicate that is winning.
Test configs before they go live, read the right log, and reload safely, on nginx, IIS or Tomcat.
“the site is throwing 502, it was fine yesterday”
It reads the real state of the server before it says anything, shows you the exact command, and waits for your approval. Windows and Linux, over the SSH access you already have.
Trace where the duplicate server blocks are defined
The include chain in nginx.conf controls which files get merged. On Linux, a typical nginx.conf contains:
http {
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
On Windows, the official distribution usually includes only conf.d, but many setups add a sites-enabled equivalent:
http {
include C:/nginx/conf/conf.d/*.conf;
include C:/nginx/conf/sites-enabled/*;
}
The include lines vary from one install to another, so run nginx -T and check the top of the output for the actual paths.
List the files and symlinks in each include directory to see what is actually active. On Linux:
ls -l /etc/nginx/sites-enabled/
ls -l /etc/nginx/conf.d/
On Windows PowerShell (adjust the path if nginx is installed elsewhere, such as C:\Program Files\nginx):
Get-ChildItem C:\nginx\conf\sites-enabled
Get-ChildItem C:\nginx\conf\conf.d
nginx -T prints each file with a comment line like # configuration file /etc/nginx/sites-enabled/example.com.conf: before its contents. Use that to map each server_name occurrence to its source file and line number.
Search all config files at once. On Linux:
grep -R "server_name" /etc/nginx/
On Windows cmd.exe:
findstr /s /i "server_name" "C:\nginx\conf\*.conf"
findstr with a wildcard may not recurse into subdirectories if the path has spaces. A more reliable PowerShell command is:
Get-ChildItem -Path C:\nginx\conf\* -Recurse | Select-String -Pattern "server_name"
Record the file path and line number for every duplicate server_name. You'll need both locations before you can decide which block to keep and which to disable.
Why nginx picks one duplicate over the other
Two server blocks with the exact same server_name and listen address produce that conflicting server name warning. Nginx then uses the block that appears first in the effective configuration. "First" means first after all includes are merged, which is normally the order of the include directives in nginx.conf. The later duplicate is ignored for that listen address.
That rule only holds when the listen address matches exactly. Two blocks with listen 80; and listen [::]:80; might not be duplicates if one is IPv4-only and the other is IPv6-only. Depending on the system and the ipv6only setting, they can be distinct sockets. A request to 127.0.0.1 matches the IPv4 block; a request to [::1] matches the IPv6 one. The warning only appears when both blocks bind the same address and port.
If the duplicates are on different ports, there is no warning and both blocks can coexist. The client's port selects the listener, and the Host header then selects among the server_name values on that listener.
default_server can mask a duplicate. A default server block does not take part in server_name matching for hosts that have no explicit match; it is the fallback. Seeing default server content for a domain that should have its own block usually means a duplicate or a missing server_name.
To verify which block answers on a specific address, send a request to that IP and port explicitly. On Linux:
curl -H "Host: example.com" http://127.0.0.1:80
curl -H "Host: example.com" http://[::1]:80
On Windows PowerShell:
Invoke-WebRequest -Headers @{Host="example.com"} http://127.0.0.1:80
Invoke-WebRequest -Headers @{Host="example.com"} http://[::1]:80
Compare the responses with what each file defines. The active block is the one whose root or proxy_pass matches the output.
Fix: remove, rename, or correct the duplicate server block
Decide which block to keep. That is usually the one with the correct root, proxy_pass, or other directives for the domain. The other block should be disabled or removed.
On Linux, if the unwanted file is enabled through a symlink in sites-enabled, unlink the symlink:
sudo unlink /etc/nginx/sites-enabled/duplicate.conf
If the duplicate is a regular file in conf.d, either move it out of the include directory or rename it so it no longer matches the *.conf pattern:
sudo mv /etc/nginx/conf.d/duplicate.conf /etc/nginx/conf.d/duplicate.conf.disabled
On Windows, rename the file so it no longer matches the include pattern. From PowerShell:
Rename-Item C:\nginx\conf\sites-enabled\duplicate.conf duplicate.conf.disabled
Or move it to a backup folder:
Move-Item C:\nginx\conf\conf.d\duplicate.conf C:\nginx\conf\backup\duplicate.conf
If the kept block needs edits, make them now. Then validate the configuration. On Linux:
sudo nginx -t
On Windows, open an elevated command prompt or PowerShell and run:
nginx -t
Once the test passes, reload nginx to pick up the change without dropping connections. On Linux with systemd:
sudo systemctl reload nginx
Alternatively:
sudo nginx -s reload
On Windows:
nginx -s reload
Re-test with the explicit Host header to confirm the correct block now serves:
curl -H "Host: example.com" http://localhost
Invoke-WebRequest -Headers @{Host="example.com"} http://localhost
Prevent future duplicate server blocks
Keep site configurations in exactly one include directory. If you use sites-enabled, do not also place the same domain in conf.d. Official nginx packages on Linux often use sites-enabled exclusively; Windows setups usually rely on conf.d. Pick one convention and document it.
Name files by domain, such as example.com.conf, and remove old symlinks when a site goes away. On Linux:
sudo unlink /etc/nginx/sites-enabled/old.example.com.conf
On Windows:
Remove-Item C:\nginx\conf\sites-enabled\old.example.com.conf
Before every reload, scan for duplicate server_name entries. On Linux:
nginx -T | grep -n "server_name example.com;"
If the output shows more than one line for the same domain on the same listen address, investigate before reloading.
On Windows cmd.exe:
nginx -T | findstr /c:"server_name example.com"
Or in PowerShell:
nginx -T | Select-String "server_name example.com"
Pay attention to the conflicting server name warning whenever you run nginx -t. In a CI pipeline, treat that warning as a failure so duplicate blocks do not reach production.
A configuration management tool that runs nginx -T, flags duplicate server blocks, and validates with nginx -t before reloading through root-owned wrappers can catch these issues across both Linux and Windows without manual searching.