Nginx Deep Dive β How It Really Works
The Secret Sauce: Event-Driven Architecture
Most old-school web servers (like Apache) work like this:
Request comes in β Create a new thread/process β Handle request β Kill thread
Imagine a restaurant where every customer gets their own personal chef. If 100 customers arrive, you need 100 chefs. Expensive and wasteful.
Nginx does it differently:
One chef (single thread) β Takes orders from ALL customers β Cooks one by one β Serves
This is called event-driven, asynchronous architecture. One worker process handles thousands of connections simultaneously using non-blocking I/O.
Why this matters for your 2GB VPS
| Server | 100 connections | 1000 connections |
|---|---|---|
| Apache | ~100 threads = ~500MB RAM | β Would crash |
| Nginx | 1-2 worker processes = ~3MB RAM | β Still sipping coffee |
Workers & Master Process
When nginx starts, it creates:
nginx (master) β The manager
βββ worker (CPU 0) β Does the actual work
βββ worker (CPU 1) β If multi-core
βββ worker (CPU 2) β Another one
- Master process: Reads config, spawns workers, never handles requests
- Worker processes: Each handles thousands of connections
- Best setting: One worker per CPU core (auto-detected)
How a Request Flows Through Nginx
Browser β http://43.133.130.60:8080/notes/home
β
[Network arrives]
β
[Nginx master passes to worker]
β
[Worker reads config]
β
βββββββ΄ββββββ
β location / β
βββββββ¬ββββββ
β
try_files:
βββ /notes/home β β No such file
βββ /notes/home.html β β
FOUND!
βββ /notes/home/ β (skipped)
β
[Read file from disk]
β
[Add headers (Content-Type, Cache-Control)]
β
[Send response back to browser]
β
[Log to access.log]
β
[Back to listening for next request]
All of this happens in microseconds.
The Config Explained Line by Line
server {
listen 8080;listen 8080 β binds to TCP port 8080. listen 443 ssl for HTTPS. listen 80 default_server for the default site.
server_name _;_ is a catch-all β respond to any domain name. Normally youβd put server_name example.com to match specific domains.
root /home/ubuntu/quartz-site/public;Every path gets prepended with this. So /notes/home.html becomes /home/ubuntu/quartz-site/public/notes/home.html.
location = / {
return 301 /notes/;
}location = /β exact match for/only (the=means βexactly thisβ)return 301β permanent redirect to/notes/
location / {
try_files $uri $uri.html $uri/ =404;
}location /β matches everything (prefix match)$uriβ the requested path (e.g.,/notes/home)try_filesβ try each option in order, first match wins
Location Matching Rules
Nginx has a specific priority for locations:
| Syntax | Example | Priority |
|---|---|---|
= /path | = / | 1st (exact) |
^~ /path | ^~ /notes/ | 2nd (prefix, no regex) |
~ pattern | ~ \.php$ | 3rd (regex, case-sensitive) |
~* pattern | ~* \.(css|js)$ | 4th (regex, case-insensitive) |
/path | / | 5th (regular prefix) |
Cache Control Explained
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|webp)$ {
expires 7d;
add_header Cache-Control "public, immutable";
}When your phone browser loads the garden:
- First visit: downloads all CSS, JS, images
- Next visit: browser checks βhas anything changed?β
- With
immutable: browser says βthis file will NEVER change in 7 days, donβt even askβ
Result: Second page load is instant π
Useful Nginx Commands
# Check config syntax
sudo nginx -t
# Reload config (zero-downtime)
sudo systemctl reload nginx
# Restart nginx
sudo systemctl restart nginx
# View error logs
sudo tail -f /var/log/nginx/error.log
# View access logs
sudo tail -f /var/log/nginx/access.log
# Test a config without applying
sudo nginx -t -c /path/to/config.conf
# Show compiled modules
nginx -VCommon Mistakes & Fixes
β502 Bad Gatewayβ
Nginx canβt reach the backend (Node.js, PHP, etc.). Since we serve static files, this shouldnβt happen.
β404 Not Foundβ
File doesnβt exist at the path. Check: is the root correct? Does the file exist?
β403 Forbiddenβ
Permission issue. Nginx runs as www-data user. Fix:
sudo chmod +rx /home/ubuntu/quartz-site/public
sudo chown -R www-data:www-data /home/ubuntu/quartz-site/publicPort already in use
sudo ss -tlnp | grep 8080 # See what's using port 8080Reverse Proxy β The Next Level
For future reference, if you ever run a Node.js app on port 3000 and want nginx to handle it:
location /api/ {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}Now visitors hit port 80/443 β nginx forwards to your app on port 3000. Clean and secure.
Related
π Back to Research