↑
Nginx for Beginners
- What Is Nginx?
- Nginx (pronounced “engine-x”) is a high-performance web server widely used for:
- serving static websites
- acting as a reverse proxy
- load balancing
- caching
- handling HTTPS/SSL
- Nginx is known for being:
- very fast
- lightweight
- scalable
- easy to configure
- It is used by companies like Netflix, Dropbox, GitHub, Airbnb, and many more.
- How Nginx Works?
- Nginx uses an
event-driven architecture instead of creating a new process for each request.
- This makes Nginx extremely efficient:
- handles thousands of connections with low memory usage
- ideal for high-traffic websites
- Common roles of Nginx:
| Nginx Role |
Description |
| Web Server |
Serves static files such as HTML, CSS, JavaScript, and images. |
| Reverse Proxy |
Forwards incoming requests to backend applications (Flask, Django, Node.js, Spring, etc.). |
| Load Balancer |
Distributes traffic across multiple backend servers to improve scalability and reliability. |
| SSL Terminator |
Handles HTTPS/TLS encryption so backend apps can stay on plain HTTP. |
- Basic Nginx Server Block (Virtual Host)
- Example: Serving a static website
server {
listen 80;
server_name example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
- This configuration:
- Listens on port 80
- Serves
/var/www/example/index.html
- Returns 404 if file not found
- Serving Static Files
- Static assets like images, CSS, JS can be served extremely fast by Nginx.
- Example:
location /static/ {
alias /var/www/example/static/;
}
alias is used instead of root when mapping a URL path to a different directory.
- Nginx as a Reverse Proxy (for Flask, Django, Node.js, etc.)
- This is one of the most common use cases:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
- What this does:
- Receives HTTP requests
- Forwards them to your backend app (Gunicorn, uWSGI, Node.js)
- Passes user IP and host headers correctly
- Enabling HTTPS with Nginx
- Install Certbot for Let's Encrypt:
sudo apt install certbot python3-certbot-nginx
- Enable HTTPS:
sudo certbot --nginx -d yourdomain.com
- Certbot:
- configures SSL automatically
- sets up renewal systemd timers
- redirects all traffic to HTTPS
- Common Nginx Commands
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
sudo systemctl status nginx
- Check configuration syntax:
sudo nginx -t
- Nginx Logging
- Default logging locations:
/var/log/nginx/access.log
/var/log/nginx/error.log
- Useful for debugging “404 Not Found”, “502 Bad Gateway”, and server errors.
- Nginx Security Basics
server_tokens off;
- Rate limiting (simple example):
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
- Hide directory listing:
autoindex off;
- Always use HTTPS in production.
Difference Between Nginx Main Config and Server Config
- Overview
- Nginx configuration is split into multiple layers, each defining different types of behavior.
- Two of the most important configuration scopes are:
Main Configuration which is global, affects the entire Nginx service
Server Configuration which applies to a specific domain or virtual host
- What Is the Main Configuration?
- Main configuration lives in:
/etc/nginx/nginx.conf
- Main config settings affect the entire Nginx system regardless of the domain or app being served.
- Common directives found in the main config:
| Directive |
Description |
user |
Specifies the system user under which Nginx worker processes run. |
worker_processes |
Defines the number of worker processes handling client requests. |
events { ... } |
Configures event-driven connection handling (e.g., max connections). |
http { ... } |
Contains global HTTP settings and includes server blocks. |
- Example:
user nginx;
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
}
- In short:
Main Configuration represents the global foundational settings.
- What Is the Server Configuration?
- Server configurations live in:
/etc/nginx/sites-available/
/etc/nginx/sites-enabled/
- Each
server { ... } block defines settings for a specific domain, application, or service.
- Server configs control:
- domain names
- SSL certificates
- proxy settings
- routing & locations
- static file paths
- Example:
server {
listen 80;
server_name example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
- In short:
Server Configuration represents per-website or per-application settings.
- Relationship Between Main Config and Server Config
- The main config provides global HTTP defaults:
http {
sendfile on;
keepalive_timeout 65;
include /etc/nginx/sites-enabled/*;
}
sites-enabled is included inside http { }, meaning all server blocks inherit the global settings.
- Server blocks override or extend what is defined in the main config.
- Key Differences at a Glance
| Main Config |
Server Config |
| Global Nginx settings |
Settings for a single website or application |
Located in /etc/nginx/nginx.conf |
Located in sites-available/ and sites-enabled/ |
| Defines worker processes, events, global http behavior |
Defines domains, paths, proxies, SSL, routing |
| Affects entire Nginx instance |
Affects only that specific server block |
| Rarely changed once set |
Frequently edited when adding domains or apps |
- When to Use Main Config
- You modify main config when you need to change:
- global Nginx behavior
- performance tuning
- connection limits
- MIME types
- compression rules
- log formatting rules
- Example use case:
- Enable Gzip for all domains
- Change worker process settings
- Configure caching engine globally
- When to Use Server Config
- You modify server config for:
- setting up a new website
- reverse proxy for an app
- SSL configuration for a domain
- custom routing (API endpoints, static content)
- Example use cases:
- Host
example.com and blog.example.com separately
- Connect Nginx to Flask/Django via
proxy_pass
- Specify a root directory for only one project
- Example Comparison
http {
sendfile on;
client_max_body_size 10M;
}
- Server config (specific domain):
server {
client_max_body_size 50M; # overrides global limit
}
- Server block overrides main config only for that domain.
Serving a Smallest App Using Nginx — Workflow
- Overview
- This chapter explains the minimal, beginner-friendly workflow to serve a simple website using Nginx.
- The “smallest app” means:
- a single
index.html file
- served from Nginx
- using the simplest valid server block
- This is the foundation for hosting more advanced applications later.
- Step 1 — Install Nginx
sudo apt update
sudo apt install nginx
- Check status:
systemctl status nginx
- Open in browser:
http://localhost
- You should see an Nginx welcome page.
- Step 2 — Create the Smallest Possible Web App
- Create a directory for your website:
sudo mkdir -p /var/www/smallapp
- Create a minimal HTML file:
<!-- /var/www/smallapp/index.html -->
<h1>Hello from Nginx!</h1>
- Set correct permissions:
sudo chown -R $USER:$USER /var/www/smallapp
- Step 3 — Create a Simple Nginx Server Block
- Create a config file for your app:
sudo vim /etc/nginx/sites-available/smallapp
- Insert this minimal configuration:
server {
listen 80;
server_name _; # matches any hostname
root /var/www/smallapp;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
- Activate the server block:
sudo ln -s /etc/nginx/sites-available/smallapp /etc/nginx/sites-enabled/
- Test your configuration:
sudo nginx -t
- Reload Nginx:
sudo systemctl reload nginx
- Step 4 — Access the Application
http://localhost
- You should see:
Hello from Nginx!
- Congratulations — you served the smallest possible app using Nginx!
- Step 5 — Optional: Use a Custom Domain (Local Testing)
- Edit your
/etc/hosts file:
127.0.0.1 myapp.local
- Update the server block:
server_name myapp.local;
- Reload Nginx again:
sudo systemctl reload nginx
- Visit:
http://myapp.local
- Step 6 — Understand the Minimal Workflow
- The minimal workflow for serving any app with Nginx is:
- Install Nginx
- Create your web application directory
- Write your app files (HTML, static files, or reverse proxy endpoint)
- Create a server block in
sites-available/
- Link it into sites-enabled
- Test configuration with
nginx -t
- Reload Nginx
- Access the site in the browser
- This same workflow applies to:
- serving SPA (Vue/React/Angular)
- hosting Python backends (Flask/Django)
- hosting Node.js backends
- hosting static assets or APIs
- Step 7 — Optional Extensions
- After serving the smallest app, you can extend the server:
- Add SSL (Let's Encrypt)
- Add reverse proxy (proxy_pass)
- Enable compression (gzip)
- Enable caching
- Add file upload size limits
- Add logging & monitoring
- But everything starts with mastering the smallest workflow.
Deploying a Frontend + Backend App Using Nginx
- Overview
- We will build a simple 2-part application:
- Frontend: a static site (HTML/CSS/JS) served by Nginx
- Backend: a small API server (Node.js example is used here)
- Nginx will act as:
- a static file server for frontend assets
- a reverse proxy that forwards
/api requests to the backend server
- This is the standard real-world deployment pattern for many modern web applications.
- Project Structure Example
- We use this simple folder structure:
myapp/
├── frontend/
│ ├── index.html
│ ├── styles.css
│ └── app.js
├── backend/
│ └── server.js
└── nginx/
└── app.conf
- You can freely rename these folders.
- Nginx will serve
frontend/ and proxy /api to backend/.
- Step 1: Create a Simple Frontend
- An extremely small frontend (
frontend/index.html):
<!DOCTYPE html>
<html>
<head>
<title>MyApp Frontend</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Hello from Frontend</h1>
<button id="btn">Call API</button>
<pre id="result"></pre>
<script src="app.js"></script>
</body>
</html>
frontend/app.js:
document.getElementById('btn').onclick = async () => {
const res = await fetch('/api/hello');
const text = await res.text();
document.getElementById('result').innerText = text;
};
- Step 2: Create a Simple Backend API
backend/server.js using Node.js:
const express = require('express');
const app = express();
const PORT = 5000;
app.get('/hello', (req, res) => {
res.send("Hello from Backend API!");
});
app.listen(PORT, () => {
console.log("Backend running on port", PORT);
});
- The backend listens on
http://localhost:5000.
- Nginx will forward
/api/* to this backend.
- Step 3: Write the Nginx Configuration File
- Place a file
nginx/app.conf:
server {
listen 80;
server_name localhost;
# -------------------------------
# 1. Serve the frontend files
# -------------------------------
root /usr/share/nginx/html;
index index.html;
# Allow direct file delivery for static assets
location / {
try_files $uri $uri/ =404;
}
# -------------------------------
# 2. Reverse proxy for backend
# -------------------------------
location /api/ {
proxy_pass http://backend:5000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
- What this config does:
root /usr/share/nginx/html → directory where frontend files live (in container)
location / → serve static frontend files
location /api/ → all requests to /api/* get forwarded to the backend server
proxy_pass http://backend:5000 → backend container hostname = backend
- Note:
- The name
backend comes from Docker Compose network resolution.
- In production Nginx normally runs inside a container, so you copy frontend files into
/usr/share/nginx/html.
- Step 4: Build the Dockerfile for the Frontend + Nginx
- Create
frontend/Dockerfile:
FROM nginx:latest
# remove default nginx welcome page
RUN rm -rf /usr/share/nginx/html/*
# copy frontend
COPY . /usr/share/nginx/html/
# copy custom nginx config
COPY ../nginx/app.conf /etc/nginx/conf.d/default.conf
- Explanation of arguments:
FROM nginx:latest → base image with nginx installed
rm -rf → remove default site
COPY . /usr/share/nginx/html/ → copy frontend files into nginx web root
COPY app.conf → override default server config
- Step 5: Write a Dockerfile for the Backend
- Create
backend/Dockerfile:
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
- Explanation:
FROM node:18 → Node.js backend
WORKDIR /app → directory inside container
COPY . . → copy backend code
npm install → install dependencies
CMD ["node","server.js"] → start backend
- Step 6: Use Docker Compose to Link Everything
- Create
docker-compose.yml at root:
version: "3.9"
services:
backend:
build: ./backend
container_name: backend
expose:
- "5000"
frontend:
build: ./frontend
container_name: frontend
ports:
- "8080:80"
depends_on:
- backend
- Explanation:
backend → backend API, accessible inside network at http://backend:5000
expose → only expose internally (not to host)
frontend runs nginx → port mapping:
8080:80 → host port 8080 → container port 80
depends_on → start backend before frontend
- After building and running, Nginx will:
- serve
/ from frontend
- proxy
/api/* to backend
- Step 7: Run Everything
docker compose up --build
- Visit frontend:
http://localhost:8080
- Click the button and you should see:
Hello from Backend API!