Skip to main content

Server Blocks

Learning Focus

Leave this lesson with a working understanding of server blocks that you can apply immediately in production.

A server block is the Nginx equivalent of an Apache VirtualHost. Each block defines the behavior for one site — which port to listen on, which domain name to match, where files are served from.


Minimal HTTP Server Block

/etc/nginx/conf.d/example.com.conf
server {
listen 80;
server_name example.com www.example.com;

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

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

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

HTTPS Server Block with HTTP Redirect

/etc/nginx/conf.d/example.com.conf
# Redirect HTTP → HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}

# HTTPS site
server {
listen 443 ssl http2;
server_name example.com www.example.com;

ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;

# Include shared SSL hardening snippet
include /etc/nginx/snippets/ssl-params.conf;

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

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

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

WordPress Server Block

/etc/nginx/conf.d/wordpress.conf
server {
listen 443 ssl http2;
server_name example.com www.example.com;

ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
include /etc/nginx/snippets/ssl-params.conf;

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

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

# WordPress main routing
location / {
try_files $uri $uri/ /index.php?$args;
}

# Block direct PHP execution in uploads
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}

# Block sensitive files
location ~* \.(env|log|ini|bak|sql|sh|conf)$ {
deny all;
}

# PHP-FPM
location ~ \.php$ {
include fastcgi.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}

# Static asset caching
location ~* \.(css|js|png|jpg|jpeg|gif|ico|webp|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
}

# HTTP → HTTPS redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}

Key Directives

DirectiveExampleWhat It Does
listen80, 443 ssl http2Port + protocol to listen on
server_nameexample.com www.example.comHostnames this block matches
root/var/www/example.com/publicDocument root for this site
indexindex.php index.htmlDefault files to serve for directory requests
try_files$uri $uri/ /index.php?$argsFile existence fallback chain
returnreturn 301 https://...Immediate redirect or response
access_log/var/log/nginx/site.logPer-site access log
error_log/var/log/nginx/site.error.log warnPer-site error log

New Site CLI Workflow

# 1. Create web root
sudo mkdir -p /var/www/example.com/public
echo "<h1>example.com</h1>" | sudo tee /var/www/example.com/public/index.html

# 2. Set ownership
sudo chown -R www-data:www-data /var/www/example.com/

# 3. Create config file
sudo vim /etc/nginx/sites-available/example.com.conf
# (paste your server block)

# 4. Enable the site
sudo ln -s /etc/nginx/sites-available/example.com.conf \
/etc/nginx/sites-enabled/example.com.conf

# 5. Test config
sudo nginx -t

# 6. Reload
sudo nginx -s reload

# 7. Verify
curl -H "Host: example.com" http://localhost/

How server_name Matching Works

Nginx selects which server block handles a request in this order:

  1. Exact matchserver_name example.com;
  2. Wildcard prefixserver_name *.example.com;
  3. Wildcard suffixserver_name example.*;
  4. Regex matchserver_name ~^www\d+\.example\.com$;
  5. Default server — the one with default_server flag, or the first block
# Mark a block as the default (catches unmatched requests)
listen 80 default_server;
# Check which server block handles a request
sudo nginx -T | grep -A5 "server_name"

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

PitfallWhat happensFix
Editing config without reloadingChanges not appliedsudo nginx -s reload after every edit
Not running nginx -t firstReload breaks with syntax errorAlways test syntax before reloading
Wrong socket path for PHP-FPM502 Bad Gatewayls /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.