Skip to content
application · webserver · proxy

The traffic director,Nginx.

A high-performance web server, reverse proxy, and load balancer that serves static content, terminates TLS, and routes traffic to upstream applications. This guide walks through a minimal server block, a wildcard virtual host, the KBVE reverse-proxy pattern, and the tuning that keeps it all fast and secure.

One instance, many hosts

A single Nginx instance can front dozens of virtual hosts, each defined by a server block that matches on server_name and port — serving static assets from disk while proxying dynamic requests upstream.

  • Reverse proxy — terminate TLS and forward with proxy_pass.
  • Load balancer — round-robin, least_conn, and ip_hash.
  • Safe reloads — nginx -t then a graceful reload.
GracefulReload
/etc/nginxConfig root
TerminatedTLS
Round-robinBalancing

Start here

Overview

Nginx is a high-performance web server and reverse proxy. It serves static assets directly from disk, terminates TLS, and forwards dynamic requests to upstream application servers over HTTP or a Unix socket. A single Nginx instance can front dozens of virtual hosts, each defined by a server block that matches on server_name and port.

This guide walks through three configurations in order of complexity: a minimal single-site block, a wildcard virtual host that serves many subdomains, and the KBVE reverse-proxy pattern that terminates TLS and load-balances upstream containers.

Four steps

Configuration Flow

  1. Write a server block — Define listen, server_name, and either a root (static) or proxy_pass (reverse proxy).

  2. Validate the syntax — Run nginx -t. Nginx checks every included file and reports the exact line on error.

  3. Reload gracefully — Run nginx -s reload or systemctl reload nginx. Workers restart without dropping active connections.

  4. Verifycurl -I https://your-host confirms the status code, TLS, and upstream headers.

Server blocks

Nginx Configuration

This is a default example of an Nginx configuration file. Each server block is self-contained; drop it in conf.d/ and reload.

The simplest useful config: serve static files from a document root over port 80.

server {
listen 80;
server_name example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

try_files attempts the exact URI, then the URI as a directory, then returns 404 — this is the correct pattern for a static site and avoids exposing the filesystem.

A wildcard server_name matches every subdomain, letting one block serve *.example.com. The captured subdomain maps to a per-tenant document root.

server {
listen 80;
server_name ~^(?<sub>.+)\.example\.com$;
root /var/www/tenants/$sub;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

The named capture (?<sub>.+) is reused in root, so a request to alice.example.com serves /var/www/tenants/alice. Pair this with a wildcard TLS certificate (*.example.com) to terminate HTTPS for every tenant from one block.

The KBVE pattern terminates TLS and reverse-proxies to an upstream application (for example a container listening on 127.0.0.1:8000). It forwards the original host and client IP so the backend sees real request metadata.

upstream kbve_app {
server 127.0.0.1:8000;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name kbve.com;
ssl_certificate /etc/nginx/ssl/kbve.crt;
ssl_certificate_key /etc/nginx/ssl/kbve.key;
location / {
proxy_pass http://kbve_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name kbve.com;
return 301 https://$host$request_uri;
}

The second server block issues a 301 redirect from HTTP to HTTPS so no plaintext traffic reaches the app. The upstream block with keepalive reuses backend connections, cutting latency under load.

Spread the load

Load Balancing

An upstream block can list multiple backends, turning Nginx into a load balancer. The default method is round-robin; Nginx also supports least_conn (send to the backend with the fewest active connections) and ip_hash (pin a client to one backend for session stickiness).

upstream kbve_pool {
least_conn;
server 10.0.0.11:8000 weight=3;
server 10.0.0.12:8000;
server 10.0.0.13:8000 backup;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name kbve.com;
location / {
proxy_pass http://kbve_pool;
proxy_set_header Host $host;
}
}
DirectiveBehavior
weight=NSend proportionally more traffic to this backend
backupOnly used when all primary backends are down
max_fails / fail_timeoutMark a backend unhealthy after N failures
least_connRoute to the least-busy backend
ip_hashSticky sessions by client IP

Fast by default

Performance: Gzip and Caching

Compression and static-asset caching are two of the highest-impact, lowest-effort SEO wins — smaller, faster responses improve Core Web Vitals, which Google uses as a ranking signal.

gzip on;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
gzip_min_length 256;
location ~* \.(?:css|js|jpg|jpeg|png|gif|ico|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}

gzip_types must list every MIME type to compress (text/html is always compressed and cannot be listed). The immutable cache directive tells browsers never to revalidate fingerprinted assets, eliminating conditional requests.

Harden the edge

Security Headers

Add baseline security headers in the server block. These harden the site against clickjacking, MIME sniffing, and mixed content, and are checked by security scanners that can influence trust signals.

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

When it breaks

Troubleshooting

  1. nginx -t fails — Read the printed file and line number. The most common cause is a missing semicolon or an unclosed block.

  2. 502 Bad Gateway — Nginx reached the proxy_pass target but got no valid response. The upstream is down or bound to a different port/socket. Check with curl 127.0.0.1:8000 on the host.

  3. 504 Gateway Timeout — The upstream is too slow. Raise proxy_read_timeout, or fix the backend.

  4. 403 Forbidden on static files — The Nginx worker user (www-data or nginx) lacks read permission on root, or SELinux is blocking it. Check ls -l and the error log.

  5. Changes not taking effect — You edited the config but did not reload. Run nginx -s reload, and confirm you edited the file actually included by nginx.conf.

Logs live at /var/log/nginx/access.log and /var/log/nginx/error.log by default — the error log is where upstream and permission failures surface.

Questions

Frequently asked

What is Nginx used for?

Nginx is a web server that also works as a reverse proxy, load balancer, and TLS terminator. It serves static files directly and forwards dynamic requests to upstream application servers.

Where is the main Nginx configuration file?

The primary file is /etc/nginx/nginx.conf. Site-specific server blocks usually live in /etc/nginx/conf.d/ or /etc/nginx/sites-available/ and are enabled by symlinking into sites-enabled.

How do I reload Nginx without dropping connections?

Run nginx -t to validate the configuration, then nginx -s reload (or systemctl reload nginx). A reload applies new config with a graceful worker restart and no dropped connections, unlike a full restart.

What is a reverse proxy in Nginx?

A reverse proxy accepts client requests and forwards them to one or more backend servers using proxy_pass. Nginx handles TLS, caching, and load balancing in front of the application, which never faces the client directly.

How do I load balance across multiple servers with Nginx?

Define an upstream block listing each backend server, then point proxy_pass at that upstream. Nginx defaults to round-robin and also supports least_conn and ip_hash. Use weight, backup, and max_fails to tune distribution and failover.

What causes a 502 Bad Gateway error in Nginx?

A 502 means Nginx reached the proxy_pass target but received no valid response — usually the upstream application is down or listening on a different port or socket. Verify the backend with curl on the host and check the Nginx error log.

How do I enable Gzip compression in Nginx?

Set gzip on and list the MIME types to compress with gzip_types (text/html is always compressed). Combine it with expires and Cache-Control headers on static assets to reduce payload size and improve Core Web Vitals.