Requests and static files
Select a server block
Understand how listen addresses and servername choose the virtual server for a request.
8 minute lesson
One Nginx instance can host many sites. Each site is a server block, and Nginx picks one block per request in two steps.
First it matches the connection against the listen directives: which address and port did the client connect to? Then, among the server blocks on that socket, it compares the request’s Host header (or the TLS server name) against each server_name. The block whose name matches wins.
If no name matches, Nginx uses the default server for that listen socket. You mark it explicitly:
server {
listen 80 default_server;
server_name _;
return 444;
}
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
}
Here blog.example.com gets the blog, and any other hostname — including requests made directly to the IP address — hits the default server. Status 444 is an Nginx special: it closes the connection without a response, a reasonable answer for traffic that isn’t addressed to any of your names.
One thing that trips people up: DNS alone does not configure Nginx. Pointing a new domain at your server changes nothing until a server block names it. The reverse is also true — you can test a server block before DNS exists.
Prove which block answers
curl --resolve lets you fake DNS for a single request, so you can test each hostname against the real server:
curl --resolve blog.example.com:80:203.0.113.10 -I http://blog.example.com/
# HTTP/1.1 200 OK
curl --resolve shop.example.com:80:203.0.113.10 -I http://shop.example.com/
# curl: (52) Empty reply from server <- the 444 default
The first request carries Host: blog.example.com and lands in the blog block. The second carries an unknown name and gets the default server’s treatment.
The classic mistake is forgetting default_server entirely. Then Nginx silently uses the first server block in configuration order as the default, and file ordering in sites-enabled/ decides which site strangers see when they scan your IP. That’s how an internal admin panel ends up answering requests for random hostnames. Always define an explicit catch-all.
Create two test hostnames pointing to one server. Use curl --resolve to prove which server block handles each name and an unknown name.
Lesson completed