Fix nginx default server catching request to subdomain
A step-by-step guide to diagnosing why nginx routes a subdomain to the default server and fixing the fall-through with config verification and safe reload.
Rao Aadil, India 7 min read
Confirm the symptom: Is the default server catching the subdomain request?
When a request to subdomain.example.com returns the default nginx page, that is nginx's default server catching the subdomain request. First confirm nginx is serving the request and inspect the response.
Use curl -I http://subdomain.example.com on Linux, or run Invoke-WebRequest -Method Head http://subdomain.example.com in Windows PowerShell. Both print response headers. Look for the Server header to verify nginx is answering. If the body or headers show the nginx welcome page, the request reached nginx but no server block matched the Host header.
Fetch the page content directly with curl http://subdomain.example.com on Linux or Invoke-WebRequest http://subdomain.example.com on Windows. If you see the default nginx page from /usr/share/nginx/html on Linux or C:\nginx\html on Windows, fall-through to the default server is likely.
Dump the active configuration next.
On Linux:
sudo nginx -T
On Windows, from the nginx installation directory in an elevated prompt or PowerShell:
cd C:\nginx
.\nginx.exe -T
This prints the full merged configuration in the order nginx loads it. Search for a server_name line containing your subdomain. If that block does not appear, nginx never loaded it. If it appears but is not first or lacks an exact match, the request may still hit another server.
“A new domain lands on the welcome page instead of your app, and the config looks right.”
It checks the server_name matching, whether the site is enabled, and whether the default server is catching the request first, which is what usually happens.
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.
Find the cause: Why does nginx fall through to the default server?
Nginx chooses a server block for each request using the Host header and the listen directives. When no server block matches the hostname, nginx uses the default server for the port. Several configuration mistakes cause this.
The server_name does not match the request host. The server block value must exactly match the hostname, or match via a wildcard or regex. Missing dots, stale domains, and typos like subdomain.example.com in the file but subdomain.example.com in the request are common. Open the intended config file and compare it character by character.
The default_server flag is set on the wrong block, or no default is explicitly defined. For every listen port, nginx chooses one default server: the block with the default_server flag on its listen line, or the first server block for that port if no flag is present. If another block appears earlier and has default_server, that block answers unmatched requests, even if your subdomain block exists. Run nginx -T and inspect every listen line for ports 80 and 443.
The site is not actually enabled. On Debian/Ubuntu, files in /etc/nginx/sites-available/ are only loaded if a symlink exists in /etc/nginx/sites-enabled/. Check with:
ls -l /etc/nginx/sites-enabled/
On RHEL/CentOS, the main /etc/nginx/nginx.conf usually includes files from /etc/nginx/conf.d/. On Windows, the default C:\nginx\conf\nginx.conf typically has an include conf.d/*.conf; line, or a similar include for your configuration directory. Verify the include line exists and the file is in the included path. On Windows, view the include lines with:
type C:\nginx\conf\nginx.conf
A duplicate server_name or load order issue. If two server blocks contain the same server_name, nginx uses the one that appears first in the merged configuration. The same can happen if a wildcard or regex block earlier in the config captures the hostname before your exact match block. Exact server_name matches have the highest priority, but a missing exact match due to a typo or an unloaded file forces nginx to fall back to wildcard, regex, or the default server.
Use nginx -T to view the final load order. The output is the merged config exactly as nginx parsed it, so it resolves include ambiguity.
Fix the configuration: Make the subdomain block match and take precedence
After identifying the cause, edit the configuration file. On Linux, the subdomain block is often in /etc/nginx/sites-available/subdomain.example.com or /etc/nginx/conf.d/subdomain.conf. On Windows, a typical path is C:\nginx\conf\conf.d\subdomain.conf.
Correct the server_name directive to the exact hostname:
server {
listen 80;
server_name subdomain.example.com;
# other directives
}
Make sure the file is actually included. On Debian/Ubuntu, create the symlink if missing:
sudo ln -s /etc/nginx/sites-available/subdomain.example.com /etc/nginx/sites-enabled/
On Windows or RHEL/CentOS, confirm nginx.conf includes the directory where your file lives. If not, add or uncomment an include line such as:
include conf.d/*.conf;
If you have an explicit catch-all block, place default_server only on that block, not on the subdomain block:
server {
listen 80 default_server;
server_name _;
return 444;
}
The subdomain block then matches its server_name; all unmatched requests go to the catch-all.
Test the configuration before reloading. On Linux:
sudo nginx -t
On Windows, from the nginx directory in an elevated prompt or PowerShell:
cd C:\nginx
.\nginx.exe -t
Only proceed if the output includes syntax is ok and test is successful. Then reload. On Linux, prefer a root-owned wrapper script if you have one; otherwise use:
sudo systemctl reload nginx
or, if you must signal nginx directly:
sudo nginx -s reload
On Windows, run from an elevated prompt or PowerShell:
cd C:\nginx
.\nginx.exe -s reload
After reloading, test the subdomain again with curl -I http://subdomain.example.com on Linux or Invoke-WebRequest -Method Head http://subdomain.example.com on Windows. Expect your application's response headers, not the nginx welcome page.
Prevent recurrence: Validate and enforce default server best practices
Make defaults explicit to avoid this later.
Always define an explicit default server for each listen port. A minimal catch-all block like the one above can live in /etc/nginx/conf.d/000-default.conf on Linux or C:\nginx\conf\conf.d\00-default.conf on Windows. The 000- or 00- prefix ensures it loads first—convenient but not required; the default_server flag does the real work.
Keep server names unique. After every configuration change, review the rendered config with nginx -T and search for the server_name lines. This catches duplicates and missing include lines before a reload.
Set up a pre-reload check that runs nginx -t automatically and refuses to reload on failure. A wrapper script or configuration management tool can enforce this. On Windows, the same nginx.exe -t and nginx.exe -s reload commands work in scripts and CI; directive syntax is identical to Linux, only file paths and invocation differ.
Document which domain maps to which server block and which block is the default for each port. When adding a new subdomain, follow the same pattern: exact server_name, no default_server on application blocks, test, then reload.
If you manage several servers, a tool that can test the config with nginx -t before any reload, identify which config file actually serves the domain across sites-available, sites-enabled, and conf.d, tail the access and error log for the specific request, and reload safely via root-owned wrapper scripts can shorten this loop. It does not edit the server block for you; you still correct server_name or default_server in the file.