LFCE 46 🐧 Nginx — Basic Configuration
Nginx configuration is built from blocks. The http block contains one or more server blocks, and each server block represents a virtual host—a site that nginx can serve. Inside a server block, location blocks define how different URL paths are handled. The directives inside these blocks control the listening port, the hostname the server responds to, the filesystem path where content is stored, and the files served when a directory is requested. A configuration that parses without error is not necessarily correct; a missing semicolon, a misplaced brace, or a server_name that does not match the request’s Host header produces a server that starts but answers incorrectly.
The previous chapter covered installation and the default welcome page. This chapter covers the configuration that replaces the default with a working site: the server block, the listen and server_name directives that determine which requests the block handles, the root and index directives that determine what content is served, and the location block that maps URL paths to filesystem paths. It also covers the validation command that prevents a syntax error from becoming an outage.
Key point: Nginx selects the server block by matching the request’s Host header against the server_name directive, and it selects the location block by matching the request’s URI against the location patterns. The listen directive determines the port and address, and the default_server parameter on listen designates the block that handles requests that match no other server name. Always run nginx -t before reloading; a configuration error prevents the new configuration from being applied, and the old configuration continues to run only if the reload is rejected.
Why basic configuration requires understanding block selection
The server-block selection problem.
A single nginx instance can serve many sites on the same IP address and port. When a request arrives, nginx reads the Host header and compares it to the server_name directives in every server block that listens on the matching address and port . If a request arrives for www.example.com and one block has server_name www.example.com;, that block handles the request. If no block matches, the request goes to the default server for that port, which is the first block unless another block has default_server on its listen directive . This is why the server_name directive must match the hostname that clients actually use, and why the default_server parameter is a deliberate choice rather than an accident.
The root-and-index problem.
The root directive tells nginx where the site’s files live on the filesystem. The index directive tells it which file to serve when the request is for a directory. A request for http://example.com/ with root /var/www/example; and index index.html; serves /var/www/example/index.html. A request for http://example.com/about.html serves /var/www/example/about.html . If the root path is wrong, nginx returns a 404 even though the configuration is syntactically valid. If the index file does not exist, nginx returns a 403 or a directory listing depending on the autoindex setting.
The location-matching problem.
The location block matches the URI portion of the request. A location / block matches every request that no more specific location matches. A location /images/ block matches requests whose URI begins with /images/. The matching is prefix-based by default, but the = modifier forces an exact match, the ~ modifier uses a case-sensitive regular expression, and the ~* modifier uses a case-insensitive regular expression . The order of evaluation is: exact match first, then the longest prefix match, then regular expressions in the order they appear in the configuration. A location block that does not match the expected requests is usually a matter of prefix versus exact versus regex.
a. The server block and the listen directive
The server block is the container for a virtual host. The listen directive inside it specifies the address and port that nginx binds to for that block.
server {
listen 80; # Listen on port 80, all addresses
server_name example.com www.example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404; # Serve file, then directory, then 404
}
}
The listen 80; directive binds the block to port 80 on all interfaces. The listen 80 default_server; form designates the block as the default for port 80, meaning it handles requests that do not match any other block’s server_name . The listen 192.168.1.10:80; form binds only to a specific address, which is useful on a multi-homed host.
The server_name directive accepts multiple hostnames separated by spaces. A request with a Host header matching any of them is handled by this block. The first block in the configuration is the default for its port unless another block declares default_server .
b. The root, index, and location directives
The root directive sets the filesystem base path for the site. The index directive sets the file that is served when the request is for a directory. The location directive maps URI patterns to configuration.
server {
listen 80;
server_name example.com;
root /var/www/example; # Base path for all requests
index index.html index.htm; # Files to try for directory requests
# Match the root path
location / {
try_files $uri $uri/ =404; # Try file, then directory, then 404
}
# Match requests beginning with /images/
location /images/ {
# Inherits root from server block
expires 30d; # Add cache headers
}
# Exact match for a specific path
location = /health {
return 200 "OK\n"; # Health check endpoint
}
# Case-insensitive regex for static assets
location ~* \.(css|js|png|jpg)$ {
expires 1h;
}
}
The try_files $uri $uri/ =404; directive is the standard pattern for serving static files. It tries the exact file requested, then the path as a directory, and returns 404 if neither exists . This avoids the directory listing behavior that would otherwise occur when a request matches a directory with no index file.
The location = /health block uses the = modifier to match only the exact URI /health. The location ~* \.(css|js|png|jpg)$ block uses a case-insensitive regular expression to match requests for static assets, regardless of case in the file extension .
c. Validating and applying the configuration
The nginx -t command parses the configuration and reports syntax errors without affecting the running server. It is mandatory before reloading.
# Validate the configuration
sudo nginx -t
# Output: nginx: configuration file /etc/nginx/nginx.conf test is successful
# Reload to apply the changes without dropping connections
sudo nginx -s reload
# Or: sudo systemctl reload nginx
If nginx -t reports an error, the reload is not attempted, and the running configuration remains in effect. This is the safety mechanism that prevents a typo from taking the site down . The error message includes the file and line number, which points directly to the problem.
The nginx -s reload command sends the reload signal to the master process. The master checks the new configuration, starts new worker processes if the configuration is valid, and signals the old workers to finish their current requests and exit . The reload is graceful: active connections are not dropped.
Complete Example Session
# ============================================
# PART 1: CREATE THE SITE DIRECTORY
# ============================================
sudo mkdir -p /var/www/example
echo "<h1>Example Site</h1>" | sudo tee /var/www/example/index.html
# ============================================
# PART 2: CREATE THE SERVER BLOCK
# ============================================
sudo tee /etc/nginx/conf.d/example.com.conf > /dev/null <<'EOF'
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location = /health {
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
EOF
# ============================================
# PART 3: DISABLE THE DEFAULT SITE (OPTIONAL)
# ============================================
# On Debian/Ubuntu, the default site may also listen on port 80
# Remove the symlink to prevent it from being the default server
sudo rm -f /etc/nginx/sites-enabled/default
# ============================================
# PART 4: VALIDATE THE CONFIGURATION
# ============================================
sudo nginx -t
# Output: nginx: configuration file /etc/nginx/nginx.conf test is successful
# ============================================
# PART 5: RELOAD NGINX
# ============================================
sudo systemctl reload nginx
# ============================================
# PART 6: TEST THE SITE LOCALLY
# ============================================
curl -H "Host: example.com" http://localhost
# Output: <h1>Example Site</h1>
# ============================================
# PART 7: TEST THE HEALTH ENDPOINT
# ============================================
curl -H "Host: example.com" http://localhost/health
# Output: OK
# ============================================
# PART 8: TEST A MISSING FILE
# ============================================
curl -I -H "Host: example.com" http://localhost/missing.html
# Output: HTTP/1.1 404 Not Found
# ============================================
# PART 9: VERIFY SERVER BLOCK SELECTION
# ============================================
# A request without a matching Host header goes to the default server
curl -I http://localhost
# Output depends on which block is the default
# ============================================
# PART 10: CHECK THE ACCESS LOG
# ============================================
sudo tail -5 /var/log/nginx/access.log
# Output shows the requests and their status codes
The ten parts demonstrate the complete configuration workflow: create content, write the server block, disable the default site, validate, reload, test the root path, test the health endpoint, test a 404, verify server-block selection, and inspect the access log. The critical step is Part 4: nginx -t catches syntax errors before the reload, and the running configuration remains in effect if the validation fails.
Quick Reference
Server Block Directives
| Directive | Purpose | Example |
|---|---|---|
listen | Address and port | listen 80; |
listen ... default_server | Default for that port | listen 80 default_server; |
server_name | Hostnames to match | server_name example.com; |
root | Document root path | root /var/www/example; |
index | Default index files | index index.html; |
location | URI matching | location / { ... } |
try_files | File resolution order | try_files $uri $uri/ =404; |
Location Modifiers
| Modifier | Match Type | Example |
|---|---|---|
| (none) | Prefix | location /images/ |
= | Exact | location = /health |
~ | Case-sensitive regex | location ~ \.php$ |
~* | Case-insensitive regex | location ~* \.(css|js)$ |
Validation and Control
| Command | Purpose |
|---|---|
nginx -t | Validate configuration syntax |
nginx -s reload | Reload without dropping connections |
systemctl reload nginx | systemd equivalent |
curl -H "Host: ..." | Test a specific server block |
Best Practices
✅ Do This:
# Validate before reloading
nginx -t # ✅
# Use try_files to prevent directory listings
try_files $uri $uri/ =404; # ✅
# Use exact match for specific endpoints
location = /health { return 200 "OK\n"; } # ✅
# Set root at the server level and inherit it
root /var/www/example; # ✅
# Explicitly set default_server when multiple blocks listen on 80
listen 80 default_server; # ✅
❌ Don’t Do This:
# Forget the semicolon after a directive
listen 80 # Syntax error; nginx -t reports it # ❌
# Omit the Host header in local tests
curl http://localhost # May hit the wrong server block # ❌
# Use a regex location without a fallback
location ~ \.php$ { ... } # Non-PHP requests have no block # ❌
# Set root inside a location that inherits from server
location / { root /other; } # Overrides server root # ❌
# Reload without validation
systemctl reload nginx # A broken config is rejected # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Site returns 404 | root path wrong or file missing | Verify the path and file permissions |
| Wrong site served | server_name mismatch or default server | Check Host header; use default_server |
nginx -t fails | Missing semicolon or brace | Read the error; it includes the file and line |
| Directory listing appears | No index file and no try_files | Add try_files $uri $uri/ =404; |
| Reload does not apply changes | Validation failed | Fix the error and reload again |
Real-World Examples
1. Minimal server block
server {
listen 80;
server_name example.com;
root /var/www/example;
index index.html;
}
2. Default server
server {
listen 80 default_server;
server_name _;
return 444;
}
3. Static file location
location / {
try_files $uri $uri/ =404;
}
4. Exact health endpoint
location = /health {
return 200 "OK\n";
add_header Content-Type text/plain;
}
5. Regex for static assets
location ~* \.(css|js|png|jpg)$ {
expires 1h;
}
6. Validate configuration
nginx -t
7. Reload nginx
systemctl reload nginx
8. Test with Host header
curl -H "Host: example.com" http://localhost
9. Check access log
tail -5 /var/log/nginx/access.log
10. Disable default site (Debian)
rm -f /etc/nginx/sites-enabled/default
Visual
Server Block Selection
┌──────────────────────────────────────────────────────────────┐
│ NGINX SELECTS THE SERVER BLOCK BY Host HEADER │
│ │
│ Request: GET / HTTP/1.1 │
│ Host: example.com │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ server { listen 80; server_name example.com; } │ │
│ │ → MATCH │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ Request: GET / HTTP/1.1 │
│ Host: unknown.com │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ No server_name match │ │
│ │ → default_server for port 80 │ │
│ │ → first block (or the one with default_server) │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
Location Matching Order
┌──────────────────────────────────────────────────────────────┐
│ LOCATION MATCHING: EXACT, PREFIX, REGEX │
│ │
│ 1. Exact match (=) │
│ location = /health → only /health │
│ │
│ 2. Longest prefix match │
│ location /images/ → /images/, /images/logo.png │
│ location / → everything else │
│ │
│ 3. Regex match (~ or ~*) │
│ location ~* \.(css|js)$ → /app.css, /main.js │
│ │
│ The most specific match wins. │
└──────────────────────────────────────────────────────────────┘
Validation and Reload Flow
┌──────────────────────────────────────────────────────────────┐
│ VALIDATE BEFORE RELOADING │
│ │
│ Edit configuration │
│ │ │
│ ▼ │
│ nginx -t │
│ │ │
│ ├── Error → Fix file, repeat │
│ │ │
│ └── Success → nginx -s reload │
│ │ │
│ ▼ │
│ New workers start │
│ Old workers finish requests │
│ No dropped connections │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Server block | Virtual host container |
listen | Address and port |
default_server | Fallback block for unmatched requests |
server_name | Hostnames matched against Host header |
root | Document root path |
index | Default file for directory requests |
location | URI matching block |
try_files | File resolution order |
| Validation | nginx -t |
| Reload | nginx -s reload or systemctl reload nginx |
Key takeaways:
- The
serverblock is selected by theHostheader. Nginx matches the request’sHostagainst theserver_namedirectives in every block that listens on the matching address and port. If no block matches, the request goes to the default server for that port . default_serveris a property of thelistenport. It designates the block that handles requests that match no other server name. Without it, the first block for that port is the default .rootandindexdetermine what is served. Therootdirective sets the filesystem base path, andindexsets the file served for directory requests. A wrong path produces a 404 even though the configuration is valid .locationmatching is ordered. Exact matches are evaluated first, then the longest prefix match, then regular expressions. The=modifier forces an exact match,~uses a case-sensitive regex, and~*uses a case-insensitive regex .try_filesprevents directory listings. The patterntry_files $uri $uri/ =404;tries the exact file, then the directory, and returns 404 if neither exists. Without it, a request for a directory with no index file may produce a listing.nginx -tis mandatory before reloading. The validation command catches syntax errors before the new configuration is applied. If it fails, the reload is not attempted, and the running configuration remains in effect .
Remember: Nginx configuration is a set of nested blocks. The http block contains server blocks, and each server block contains location blocks. The listen and server_name directives determine which requests the block handles. The root and index directives determine what content is served. The location directives determine how different URI paths are handled. Validate with nginx -t, reload with nginx -s reload, and test with curl using the correct Host header. A configuration that parses is not necessarily a configuration that serves the right site; the test confirms both.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!