Chuyển đến nội dung chính

Lesson 2: Basic Nginx Configuration

A lesson on Nginx configuration with nginx.conf syntax, contexts (http/server/location), and basic directives. Guide to creating virtual hosts, serving static files, index files, autoindex, and custom error pages. Includes practical examples and production best practices.

🔒 DevSecOps — Lesson 2 Lesson 2: Basic Nginx Configuration

Nginx from Basics to Advanced

Part 1: Basics

xdev.asia

1. nginx.conf Configuration File Syntax

The nginx.conf file is the heart of Nginx, defining the entire behavior of the web server. Understanding the configuration syntax is the first step to mastering Nginx.

1.1. Basic Structure

# Directive đơn giản (simple directive)
worker_processes 4;

Directive block (block directive)

events { worker_connections 1024; }

Block lồng nhau (nested blocks)

http { server { location / { root /var/www/html; } } }

1.2. Syntax Rules

1. Directives:

  • Each directive ends with a semicolon ;
  • Directives can be simple (one line) or block (with {})
  • Case-sensitive: Root differs from root
# Correct
worker_processes 2;

Wrong - missing semicolon

worker_processes 2

Wrong - incorrect case

Worker_Processes 2;

2. Comments:

# This is a single-line comment
worker_processes 4;  # End-of-line comment

No multi-line comments in Nginx

Must use # for each line

3. Include files:

# Include another file
include /etc/nginx/mime.types;

Include multiple files with wildcard

include /etc/nginx/conf.d/.conf; include /etc/nginx/sites-enabled/;

4. Variables:

# Nginx has many built-in variables

Start with $

$remote_addr # Client IP $request_uri # Requested URI $host # Hostname

Usage example

location / { return 200 "Your IP: $remote_addr\n"; }

5. String values:

# No quotes needed for simple values
root /var/www/html;

Quotes required if there are spaces or special characters

error_log "/var/log/nginx/error.log" warn; add_header X-Custom-Header "Hello World";

Can use single or double quotes

root '/var/www/html'; root "/var/www/html";

1.3. Units and Sizes

# Time units
client_body_timeout 60s;      # seconds (default)
client_body_timeout 60;       # also seconds
client_body_timeout 60m;      # minutes
client_body_timeout 1h;       # hours
client_body_timeout 1d;       # days

Size units

client_max_body_size 10m; # megabytes client_max_body_size 10M; # also megabytes client_max_body_size 1g; # gigabytes client_max_body_size 1024k; # kilobytes client_max_body_size 1048576; # bytes (no unit)

1.4. Measurement units

# No unit = bytes
client_max_body_size 1048576;  # 1MB

k/K = kilobytes

client_max_body_size 1024k;

m/M = megabytes

client_max_body_size 1m;

g/G = gigabytes (Nginx 0.7.0+)

client_max_body_size 1g;


2. Contexts and Directives

Nginx uses a context system to organize configuration by level. Each context defines the scope of applicable directives.

2.1. Main Contexts

# MAIN CONTEXT (global)
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

EVENTS CONTEXT

events { worker_connections 1024; use epoll; }

HTTP CONTEXT

http { # Applies to all HTTP traffic

# SERVER CONTEXT
server {
    # Applies to a specific virtual host
    
    # LOCATION CONTEXT
    location / {
        # Applies to a specific URL pattern
    }
}

}

STREAM CONTEXT (for TCP/UDP)

stream { server { listen 3306; } }

MAIL CONTEXT (for mail proxy)

mail { server { listen 25; } }

2.2. HTTP Context - Global Configuration

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"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

# Gzip compression
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript;

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

# Include server blocks
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

}

2.3. Server Context - Virtual Host

http {
# Server block 1
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;

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

# Server block 2
server {
    listen 80;
    server_name blog.example.com;
    root /var/www/blog;
}

# Default server (catch-all)
server {
    listen 80 default_server;
    server_name _;
    return 444;  # Close connection
}

}

2.4. Location Context - URL Matching

server {
listen 80;
server_name example.com;
root /var/www/html;

# Exact match
location = /about {
    # Matches only /about
}

# Prefix match
location /images/ {
    # Matches /images/*, /images/photo.jpg, etc.
}

# Regex match (case-sensitive)
location ~ \.(jpg|png|gif)$ {
    # Matches files ending with .jpg, .png, .gif
}

# Regex match (case-insensitive)
location ~* \.(jpg|png|gif)$ {
    # Matches JPG, jpg, JpG, etc.
}

# Prefix match (stop regex checking)
location ^~ /api/ {
    # Matches /api/* and stops regex checking
}

# Default location
location / {
    # Matches everything if no other match
}

}

2.5. Location Matching Priority

Nginx processes locations in priority order:

  1. = - Exact match (highest)
  2. ^ - Prefix match (stop regex)
  3. or ~* - Regex match (in order of appearance in file)
  4. No modifier - Prefix match (lowest)

Illustrative example:

server {
listen 80;
server_name example.com;

# Priority 1 - Exact match
location = /test {
    return 200 "Exact match: /test\n";
}

# Priority 2 - Prefix (stop regex)
location ^~ /test {
    return 200 "Prefix match (^~): /test*\n";
}

# Priority 3 - Regex (case-insensitive)
location ~* ^/test {
    return 200 "Regex match (~*): /test*\n";
}

# Priority 4 - Prefix match
location /test {
    return 200 "Prefix match: /test*\n";
}

# Default
location / {
    return 200 "Default location\n";
}

}

Test results:

curl http://example.com/test

→ "Exact match: /test"

curl http://example.com/test123

→ "Prefix match (^): /test*" (because ^ stops regex)

If removing the ^~ location:

curl http://example.com/test123

→ "Regex match (~): /test"


3. Configuring Virtual Hosts (Server Blocks)

Virtual hosts allow a single Nginx server to serve multiple websites and domains.

3.1. Creating Your First Virtual Host

Step 1: Create the website directory

# Create document root
sudo mkdir -p /var/www/mysite.com/html

Create logs directory

sudo mkdir -p /var/www/mysite.com/logs

Set ownership

sudo chown -R $USER:$USER /var/www/mysite.com sudo chmod -R 755 /var/www/mysite.com

Step 2: Create a sample HTML file

cat > /var/www/mysite.com/html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Welcome to mysite.com</title>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
h1 { color: #00539C; }
</style>
</head>
<body>
<h1>Welcome to mysite.com!</h1>
<p>This is my first Nginx virtual host.</p>
</body>
</html>
EOF

Step 3: Create the virtual host configuration file

# Ubuntu/Debian
sudo nano /etc/nginx/sites-available/mysite.com

CentOS/RHEL

sudo nano /etc/nginx/conf.d/mysite.com.conf

Configuration file contents:

server {
# Port and server name
listen 80;
listen [::]:80;
server_name mysite.com www.mysite.com;

# Document root
root /var/www/mysite.com/html;
index index.html index.htm;

# Logs
access_log /var/www/mysite.com/logs/access.log;
error_log /var/www/mysite.com/logs/error.log;

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

# Error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

location = /404.html {
    internal;
}

location = /50x.html {
    internal;
}

# Deny access to hidden files
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

}

Step 4: Enable the virtual host (Ubuntu/Debian)

# Create symlink
sudo ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/

Check configuration

sudo nginx -t

Reload Nginx

sudo systemctl reload nginx

Step 5: Configure DNS or hosts file

# Add to /etc/hosts (for local testing)
sudo nano /etc/hosts

Add the line:

127.0.0.1 mysite.com www.mysite.com

Step 6: Test

curl http://mysite.com

or open browser: http://mysite.com

3.2. Virtual Host with Multiple Domains

# Config 1: Multiple domains for the same content
server {
listen 80;
server_name mysite.com www.mysite.com example.com www.example.com;
root /var/www/mysite.com/html;
index index.html;
}

Config 2: Subdomain

server { listen 80; server_name blog.mysite.com; root /var/www/blog; index index.html; }

server { listen 80; server_name shop.mysite.com; root /var/www/shop; index index.html; }

Config 3: Wildcard subdomain

server { listen 80; server_name *.mysite.com; root /var/www/subdomains/$host;

# $host will contain subdomain.mysite.com

}

Config 4: Regex server name

server { listen 80; server_name ~^(www.)?(?<domain>.+)$; root /var/www/$domain; }

3.3. Default Server (Catch-all)

# Default server to handle unmatched requests
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;  # Underscore = don't care about server name

# Option 1: Return 444 (close connection)
return 444;

# Option 2: Return 403 Forbidden
# return 403;

# Option 3: Redirect to main site
# return 301 https://mainsite.com$request_uri;

# Option 4: Show maintenance page
# root /var/www/default;
# index maintenance.html;

}

3.4. Advanced Listen Directives

server {
# IPv4
listen 80;

# IPv6
listen [::]:80;

# Specific IP
listen 192.168.1.100:80;

# Different port
listen 8080;

# Default server
listen 80 default_server;

# SSL
listen 443 ssl;
listen [::]:443 ssl;

# HTTP/2
listen 443 ssl http2;

# Multiple options
listen 80 default_server reuseport;

}


4. Serving Static Files

Nginx excels at serving static content (HTML, CSS, JS, images).

4.1. Basic Configuration

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

# Document root
root /var/www/static;

# Index files
index index.html index.htm;

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

}

Directory structure:

/var/www/static/
├── index.html
├── css/
│   ├── style.css
│   └── bootstrap.css
├── js/
│   ├── app.js
│   └── jquery.js
└── images/
├── logo.png
└── background.jpg

Requests handled:

http://static.example.com/              → /var/www/static/index.html
http://static.example.com/css/style.css → /var/www/static/css/style.css
http://static.example.com/images/logo.png → /var/www/static/images/logo.png

4.2. Root vs Alias

Root directive:

location /images/ {
root /var/www/static;
}

Request: /images/photo.jpg

File path: /var/www/static/images/photo.jpg

(root + location path)

Alias directive:

location /images/ {
alias /var/www/photos/;
}

Request: /images/photo.jpg

File path: /var/www/photos/photo.jpg

(alias replaces location path)

Detailed example:

server {
listen 80;
server_name example.com;

# Using root
location /static/ {
    root /var/www;
}
# /static/style.css → /var/www/static/style.css

# Using alias
location /assets/ {
    alias /var/www/static/;
}
# /assets/style.css → /var/www/static/style.css

# Alias for exact path
location = /favicon.ico {
    alias /var/www/icons/favicon.ico;
}

}

Note: When using alias, the location path must end with / if alias also ends with /.

4.3. Try_files Directive

# Syntax
try_files file ... uri;
try_files file ... =code;

Example 1: Check file, folder, then 404

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

Example 2: Fallback to index.html (SPA)

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

Example 3: Check multiple files

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

Example 4: Fallback to backend

location / { try_files $uri $uri/ @backend; }

location @backend { proxy_pass http://localhost:3000; }

4.4. Configuration per File Type

server {
listen 80;
server_name cdn.example.com;
root /var/www/cdn;

# HTML files
location ~ \.html$ {
    add_header Cache-Control "public, max-age=3600";
}

# CSS and JavaScript
location ~ \.(css|js)$ {
    add_header Cache-Control "public, max-age=31536000";
    access_log off;
}

# Images
location ~ \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
    add_header Cache-Control "public, max-age=31536000";
    access_log off;
    expires 1y;
}

# Fonts
location ~ \.(woff|woff2|ttf|otf|eot)$ {
    add_header Cache-Control "public, max-age=31536000";
    add_header Access-Control-Allow-Origin "*";
    access_log off;
}

# Videos
location ~ \.(mp4|webm|ogg)$ {
    add_header Cache-Control "public, max-age=31536000";
    mp4;  # Enable MP4 streaming
    access_log off;
}

# Downloads
location /downloads/ {
    add_header Content-Disposition "attachment";
}

}

4.5. Security for Static Files

server {
listen 80;
root /var/www/html;

# Deny access to hidden files
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

# Deny access to backup files
location ~ ~$ {
    deny all;
    access_log off;
    log_not_found off;
}

# Deny access to config files
location ~ \.(conf|config|yml|yaml|ini)$ {
    deny all;
}

# Protect sensitive directories
location ~ ^/(\.git|\.svn|\.env) {
    deny all;
}

}


5. Configuring Index Files and Autoindex

5.1. Index Directive

# Syntax
index file ...;

Example 1: Default index

server { listen 80; root /var/www/html; index index.html index.htm; }

Example 2: Multiple index files (in order)

server { listen 80; root /var/www/html; index index.php index.html index.htm default.html; }

Example 3: Different index files per location

server { listen 80; root /var/www/html;

location / {
    index index.html;
}

location /blog/ {
    index index.php;
}

location /docs/ {
    index readme.md index.html;
}

}

5.2. Autoindex (Directory Listing)

# Enable autoindex
server {
listen 80;
server_name files.example.com;
root /var/www/files;

location / {
    autoindex on;
}

}

Detailed autoindex configuration

location /downloads/ { autoindex on; # Enable directory listing autoindex_exact_size off; # Show size in KB, MB instead of bytes autoindex_localtime on; # Show local time instead of GMT autoindex_format html; # Format: html, xml, json, jsonp }

Example with JSON format

location /api/files/ { autoindex on; autoindex_format json; }

Autoindex output:

Index of /downloads/

../ file1.pdf 23-Nov-2024 10:30 2.5M file2.zip 22-Nov-2024 15:45 15M folder/ 20-Nov-2024 09:00 -

5.3. Custom Autoindex Styling

server {
listen 80;
root /var/www/files;

location / {
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
    
    # Add custom header/footer
    add_before_body /autoindex/header.html;
    add_after_body /autoindex/footer.html;
}

location /autoindex/ {
    internal;
    alias /var/www/autoindex/;
}

}

header.html file:

<!DOCTYPE html>
<html>
<head>
<title>File Directory</title>
<style>
body { font-family: Arial; margin: 20px; }
h1 { color: #333; }
a { color: #0066cc; text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>File Directory</h1>
<hr>

footer.html file:

    <hr>
<p>© 2024 My Company</p>
</body>
</html>

6. Custom Error Pages

6.1. Basic Configuration

server {
listen 80;
server_name example.com;
root /var/www/html;

# Custom error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

# Location for error pages
location = /404.html {
    internal;  # Only accessible internally
}

location = /50x.html {
    internal;
}

}

6.2. Detailed Error Pages

Create 404.html file:

cat > /var/www/html/404.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>404 - Page Not Found</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
background: #f5f5f5;
}
h1 { font-size: 72px; color: #e74c3c; }
p { font-size: 24px; color: #555; }
a { color: #3498db; text-decoration: none; }
</style>
</head>
<body>
<h1>404</h1>
<p>Oops! Page not found.</p>
<p><a href="/">← Go back home</a></p>
</body>
</html>
EOF

Create 50x.html file:

cat > /var/www/html/50x.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>500 - Server Error</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 50px;
background: #f5f5f5;
}
h1 { font-size: 72px; color: #e67e22; }
p { font-size: 24px; color: #555; }
</style>
</head>
<body>
<h1>500</h1>
<p>Internal Server Error</p>
<p>We're working on it!</p>
</body>
</html>
EOF

6.3. Advanced Error Pages

server {
listen 80;
server_name example.com;
root /var/www/html;

# Error pages per location
location / {
    error_page 404 /errors/404.html;
}

location /api/ {
    error_page 404 /errors/api-404.json;
    error_page 500 /errors/api-500.json;
}

# Error page with custom message
location /special/ {
    error_page 404 =200 /custom-404.html;
    # =200 overrides the status code
}

# Redirect to external error page
location /old-site/ {
    error_page 404 = @external_error;
}

location @external_error {
    return 302 https://example.com/error-handler;
}

# Error page with variable
location /dynamic/ {
    error_page 404 /404.html?page=$uri;
}

# Named location for errors
error_page 404 = @notfound;

location @notfound {
    return 404 "Custom 404 message\n";
}

}

6.4. Error Log with Format

http {
# Define custom error log format
log_format error_log '[$time_local] $status $request '
'Client: $remote_addr '
'Server: $server_name';

server {
    listen 80;
    server_name example.com;
    
    # Use custom format
    error_log /var/log/nginx/example.error.log error_log;
    
    # Different log level
    error_log /var/log/nginx/debug.log debug;
}

}


7. Practice Exercises

Exercise 1: Create a Virtual Host

  1. Create a virtual host for mysite.local
  2. Document root: /var/www/mysite
  3. Create an index.html file with any content
  4. Add to /etc/hosts and test

Exercise 2: Static File Server

  1. Create the directory structure:
/var/www/static/
├── index.html
├── css/style.css
├── js/app.js
└── images/logo.png
  1. Configure Nginx to serve these files
  2. Set different cache headers for each file type

Exercise 3: Directory Listing

  1. Create a virtual host for files.local
  2. Enable autoindex
  3. Customize the format and styling
  4. Test with multiple files

Exercise 4: Custom Error Pages

  1. Create custom 404 and 500 pages
  2. Apply to a virtual host
  3. Test by accessing a non-existent URL
  4. Test a 500 error (can fake with return 500)

Exercise 5: Multiple Virtual Hosts

  1. Create 3 virtual hosts:
    • site1.local → /var/www/site1
    • site2.local → /var/www/site2
    • blog.site1.local → /var/www/blog
  2. Each site has different content
  3. Configure and test all of them

8. Common Troubleshooting

Error 1: 403 Forbidden

# Cause: Permission
ls -la /var/www/html

Fix: Set correct ownership

sudo chown -R www-data:www-data /var/www/html sudo chmod -R 755 /var/www/html

Cause: SELinux (CentOS)

sudo setenforce 0

Error 2: 404 Not Found

# Check root directive
location / {
root /var/www/html;  # Is this path correct?
index index.html;    # Does this file exist?
}

Check with curl

curl -I http://example.com

Error 3: Configuration Not Reloading

# Test config first
sudo nginx -t

If OK, reload

sudo systemctl reload nginx

Check error log

sudo tail -f /var/log/nginx/error.log

Error 4: Server Name Not Working

# Check DNS/hosts
cat /etc/hosts

Check server_name directive

grep server_name /etc/nginx/sites-available/*

Clear browser cache

Or test with curl

curl -H "Host: mysite.com" http://localhost


9. Best Practices

  1. Organize configuration files:
/etc/nginx/
├── nginx.conf (main config)
├── conf.d/ (global configs)
└── sites-available/ (individual sites)
  1. Clear comments:
# Block spam bots
if ($http_user_agent ~* (bot|crawler|spider)) {
return 403;
}
  1. Use includes:
http {
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
}
  1. Test before reloading:
sudo nginx -t && sudo systemctl reload nginx
  1. Backup configs:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup

Summary

In this lesson, you learned:

  • ✅ nginx.conf syntax and structure
  • ✅ Contexts and directives in Nginx
  • ✅ Creating and managing virtual hosts
  • ✅ Serving static files efficiently
  • ✅ Configuring index files and autoindex
  • ✅ Customizing error pages

Next lesson: We will explore Logging and Monitoring — how to track and analyze traffic on your Nginx server.