Understanding Nginx Server Blocks: Common Configuration Questions

Understand how Nginx server blocks work — how requests are matched to the right domain, what goes inside a block, and how to organize multi-site configs without confusion.

Understanding Nginx Server Blocks: Common Configuration Questions

Understanding Nginx server blocks is one of the fastest ways to make your Nginx configuration feel less mysterious. Server blocks decide which site handles a request, which domain names match, which files are served, and where proxy traffic goes.

If you host more than one domain, redirect HTTP to HTTPS, or run apps behind Nginx, server blocks are where most of that routing begins.

What Is an Nginx Server Block?

An Nginx server block is a section of configuration that defines how Nginx should respond for a specific address, port, and hostname combination.

A basic static site server block looks like this:

server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

This tells Nginx to listen on port 80, match requests for example.com and www.example.com, serve files from the public directory, and return 404 when a file is not found.

The name "server block" is common in Nginx. Apache users may know a similar idea as a virtual host. The purpose is the same: allow one web server to handle multiple sites or apps.

Server blocks are usually stored in /etc/nginx/sites-available/ and enabled with symlinks into /etc/nginx/sites-enabled/ on Debian and Ubuntu systems. Some distributions use /etc/nginx/conf.d/ instead. The exact layout is less important than knowing which files are included by nginx.conf.

How Does Nginx Choose the Right Server Block?

Nginx selection happens in stages. First, it considers the IP address and port from the listen directive. Then it compares the request hostname to server_name.

If no hostname matches, Nginx uses the default server for that address and port. You can mark one explicitly:

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

Some teams use a default block like this to drop unmatched requests. Others return a plain 404. The best choice depends on your logging and security preferences.

If you are still learning, a visible 404 default can be easier to debug than return 444, because 444 is an Nginx-specific connection close and can look like a network problem from the client side. In production, some teams prefer the quiet close for random unmatched hosts. Either choice is fine if the team understands it.

Exact names are preferred over wildcard names. For example, api.example.com is more specific than *.example.com. Regex server names are possible, but they should be rare because they are harder to read and troubleshoot.

A common mistake is adding a new domain to DNS but forgetting to add it to server_name. The request reaches the server, but Nginx sends it to the default block. From the user's view, the wrong site appears or the request fails.

Another common mistake is creating two blocks that both claim the same listen and server_name. Nginx may warn about conflicting server names and ignore one of them. Always test configuration after adding a site.

Use this when the wrong site answers:

sudo nginx -T | grep -n "server_name"
curl -I -H 'Host: example.com' http://127.0.0.1/

nginx -T prints the full loaded configuration, including included files. That is often faster than opening every file under sites-enabled.

What Goes Inside a Server Block?

A server block usually contains directives for domain matching, TLS, document root, logging, redirects, and one or more location blocks.

For a reverse proxy, the block may look like this:

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:3000;
    }
}

Here, Nginx terminates HTTPS and forwards traffic to a local application on port 3000. This is common for Node.js, Python, Ruby, Go, and Java apps.

If the upstream app needs to know the original scheme or client IP, those proxy_set_header lines matter. Without them, the app may think every request came from 127.0.0.1 over plain HTTP. That can break redirects, audit logs, rate limits, and generated callback URLs.

Use clear log paths when hosting multiple sites:

access_log /var/log/nginx/app.example.com.access.log;
error_log /var/log/nginx/app.example.com.error.log;

Separate logs make troubleshooting much easier. When every site writes to one access log, it takes longer to spot which domain is failing.

For a busy host, this also makes retention easier. You may want detailed logs for an API during an incident and lighter logging for a static site. Keeping the files separate gives you that option without changing every server block at once.

How Are Server Blocks Different from Location Blocks?

Server blocks choose the site. Location blocks choose what to do with paths inside that site.

For example:

server {
    server_name example.com;

    location /assets/ {
        expires 30d;
    }

    location /api/ {
        proxy_pass http://api_backend;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Requests for /assets/logo.png get static file caching. Requests for /api/users go to an upstream backend. Other requests fall through to the frontend app.

This split is important. If the wrong domain answers, look at server block matching. If the right domain answers but the wrong path behavior happens, look at location matching.

This distinction saves time. A request for api.example.com/users can fail because api.example.com matched the wrong server block, or because /users matched the wrong location inside the right block. Those are different problems.

For more detail on path routing, see Nginx location blocks explained.

How Should I Organize Multiple Sites?

Use one file per site or app when possible. Give each file a name that matches the domain or purpose:

/etc/nginx/sites-available/example.com
/etc/nginx/sites-available/api.example.com
/etc/nginx/sites-available/admin.example.com

This makes reviews easier and reduces the chance of changing the wrong service. It also helps when you need to disable a site quickly.

Keep shared snippets small and obvious. TLS settings, proxy headers, and security headers are good candidates for includes. Avoid hiding major routing behavior in a generic include file, because it makes debugging harder.

A practical setup might include:

include snippets/proxy-headers.conf;
include snippets/security-headers.conf;

Use includes to reduce repetition, not to make every site behave identically. An internal admin panel, public API, and static marketing site often need different limits and headers.

Before deleting or renaming a server block file, check how it is included. On Debian-style layouts, removing the file from sites-available does nothing if another copy still exists in conf.d. On the other hand, deleting a symlink in sites-enabled disables the site without deleting the source file.

ls -l /etc/nginx/sites-enabled/
sudo nginx -T | grep -n "include"

That quick check prevents the frustrating case where you edit the right-looking file and Nginx keeps using a different one.

When to Get Help with Server Block Problems

Ask a DevOps engineer or Nginx specialist for help when server block changes affect production domains, HTTPS certificates, customer-facing redirects, or multiple applications on the same host. Small mistakes can send users to the wrong app or break certificate renewal.

You should also get help if Nginx keeps serving the wrong site after you think the configuration is correct. The issue may involve DNS, IPv6, a load balancer, a CDN, or an include file you did not notice.

Server blocks are the map Nginx uses to route domain-level traffic. Keep them specific, test after each change, use separate logs, and separate domain matching from path routing in your mental model. Once that clicks, most Nginx configuration questions become much easier to answer.