nginx.conf Structure
Learning Focus
Leave this lesson with a working understanding of nginx.conf structure that you can apply immediately in production.
Nginx configuration uses a hierarchical block system. Understanding the context hierarchy is fundamental — directives in outer blocks are inherited by inner blocks, but inner blocks can override them.
Context Hierarchy
Main context (top of file — global settings)
└── events { } (connection handling)
└── http { } (all HTTP traffic)
└── server { } (one site / virtual host)
└── location { } (URL-path specific rules)
Directives only work inside their allowed context. Putting listen inside http {} is an error.
Annotated nginx.conf Template
/etc/nginx/nginx.conf
# ============================================================
# MAIN CONTEXT — global settings
# ============================================================
# User and group the worker processes run as
user www-data;
# Number of worker processes — match CPU count (or use auto)
worker_processes auto;
# Max number of open file descriptors per worker
worker_rlimit_nofile 65535;
# PID file location
pid /run/nginx.pid;
# Dynamic modules (Debian/Ubuntu)
include /etc/nginx/modules-enabled/*.conf;
# ============================================================
# EVENTS CONTEXT — connection model
# ============================================================
events {
# Max simultaneous connections per worker
# Total capacity = worker_processes × worker_connections
worker_connections 1024;
# Accept multiple connections per event (recommended)
multi_accept on;
# Connection processing method (epoll is best on Linux)
use epoll;
}
# ============================================================
# HTTP CONTEXT — all HTTP settings
# ============================================================
http {
# MIME types
include /etc/nginx/mime.types;
default_type application/octet-stream;
# ---- Logging ----
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# ---- Performance ----
sendfile on; # Efficient file serving via kernel
tcp_nopush on; # Send headers + start of file together
tcp_nodelay on; # Disable Nagle for keep-alive
# ---- Keep-Alive ----
keepalive_timeout 65;
keepalive_requests 1000;
# ---- Compression ----
gzip on;
gzip_types text/plain text/css application/json
application/javascript text/xml
application/xml image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_vary on;
# ---- Security ----
server_tokens off; # Hide Nginx version in headers/error pages
# ---- Buffer Sizes ----
client_max_body_size 64m;
client_body_buffer_size 128k;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# ---- Timeouts ----
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
# ---- Include Site Configs ----
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Key Directives Reference
Main Context
| Directive | Example | What It Does |
|---|---|---|
user | www-data | Worker process user |
worker_processes | auto | Number of workers (match CPU cores) |
worker_rlimit_nofile | 65535 | Max open files per worker |
pid | /run/nginx.pid | PID file path |
error_log | /var/log/nginx/error.log warn | Server-level error log |
Events Context
| Directive | Example | What It Does |
|---|---|---|
worker_connections | 1024 | Max connections per worker |
multi_accept | on | Accept multiple connections per iteration |
use | epoll | Linux event model (epoll is optimal) |
HTTP Context (Most Common)
| Directive | Example | What It Does |
|---|---|---|
sendfile | on | Kernel-level file sending (faster) |
tcp_nopush | on | Batch send headers + data |
tcp_nodelay | on | Low latency for keep-alive |
keepalive_timeout | 65 | How long to keep idle connections |
gzip | on | Enable gzip compression |
server_tokens | off | Hide Nginx version |
client_max_body_size | 64m | Max upload size |
Apply Changes
# Always test before reloading
sudo nginx -t
# Graceful reload (zero downtime)
sudo nginx -s reload
# Or via systemctl
sudo systemctl reload nginx
# Full restart (drops active connections)
sudo systemctl restart nginx
# Dump the complete merged config (debug tool)
sudo nginx -T
Common Mistakes
| Mistake | Error | Fix |
|---|---|---|
| Missing semicolon at end of directive | nginx: [emerg] ... | Add ; after every directive |
| Unclosed brace | Config parse error | Match every { with } |
| Wrong context for directive | directive not allowed here | Move to correct context |
Forgetting include for site configs | Sites not loaded | Check include paths in nginx.conf |
Setting worker_processes too high | CPU contention | Use auto or match core count |
Hands-On Practice
# Verify Nginx is running
sudo systemctl status nginx
# Test config syntax
sudo nginx -t
# Reload without downtime
sudo nginx -s reload
# Check error log
sudo tail -20 /var/log/nginx/error.log
Common Pitfalls
| Pitfall | What happens | Fix |
|---|---|---|
| Editing config without reloading | Changes not applied | sudo nginx -s reload after every edit |
Not running nginx -t first | Reload breaks with syntax error | Always test syntax before reloading |
| Wrong socket path for PHP-FPM | 502 Bad Gateway | ls /run/php/ and verify the exact socket filename |
What's Next
- Continue to the next lesson in this module, or go to the module index for an overview.
- Use the Cheatsheets for quick CLI reference.