How to Use Nginx: The Beginner's Guide (First Server Block, Static Files, Reverse Proxy, and HTTPS)

CodexFuse Team · 2026-10-31T03:57:27.257Z · 13 min read

How do you use Nginx? The 6-step beginner arc

Learning how to use Nginx means learning to install it on a Linux server, write a server block that tells it what to serve and for which domain, enable the site, test the config before reloading, and then layer on a reverse proxy and HTTPS when you need them. Nginx (pronounced "engine-x") is the open-source web server that sits in front of most of the modern web — serving static files, proxying requests to apps, and terminating HTTPS. This nginx tutorial is the complete beginner arc in one page, in the exact order you will do it on a real server:

  1. Install Nginx on Ubuntu or Debian with sudo apt install nginx.
  2. Write your first server block — a small file in /etc/nginx/sites-available/ with a listen 80 line, your domain, and a document root.
  3. Enable it and test: symlink it into sites-enabled, run sudo nginx -t, then sudo systemctl reload nginx.
  4. Serve your static files with root, index, and try_files so missing pages return a clean 404.
  5. Add a reverse proxy with proxy_pass when you have a real app (Node, Python, anything) running behind Nginx.
  6. Switch on HTTPS with sudo certbot --nginx -d yourdomain.com — free Let's Encrypt certificates with automatic renewal.

If you are starting from nothing but an idea and a domain, first read our what is VPS hosting guide — Nginx runs on that server, and the guide explains the machine everything below is installed on.

What is Nginx, exactly?

Nginx is an open-source web server, reverse proxy, and load balancer first released in 2004 by Russian developer Igor Sysoev. It is a web server when it serves files from disk, a reverse proxy when it forwards requests to backend apps, and a load balancer when it spreads those requests across several backends. One program, three jobs, and it handles all of them with remarkably low memory. The current releases as of October 30, 2026 are mainline 1.31.8 and stable 1.30.6, per the official nginx.org download page — your distro's package will usually carry a stable release close to that.

Under the hood, Nginx runs one master process and a pool of worker processes — typically one worker per CPU core — and each worker handles thousands of connections through an asynchronous, event-driven loop instead of a thread per request. That architecture is the whole reason it won: when thousands of browsers connect at once, Nginx stays calm where older thread-per-request servers started thrashing. You do not need to configure this to start; the defaults are right for a beginner and the master process takes care of starting the workers for you.

What do you need before you start?

You need a Linux server with root or sudo access — Ubuntu 22.04 or newer (or Debian 11+) is the path this guide teaches, because the package layout below is the Debian/Ubuntu one. You also need a domain name pointed at your server's IP address for the HTTPS section, and comfort running commands over SSH. A static website or a small finished app to put online makes the exercises real; Nginx itself needs nothing else. On Red Hat-family systems (RHEL, Rocky, AlmaLinux) the layout differs slightly — sites go in /etc/nginx/conf.d/ rather than the sites-available pattern taught here — so pick one distro's path and never mix the two.

How do I install Nginx and check it works?

Install Nginx with two commands, and it starts serving immediately. On Ubuntu or Debian:

sudo apt update
sudo apt install nginx -y

The package starts Nginx for you on install and enables it to start at boot. Verify it is running:

sudo systemctl status nginx

Then open http://your_server_ip in a browser — you should see Nginx's default welcome page. That page is served from the default server block the package ships, and replacing it with your own site is the next section. If the welcome page does not load, check that your cloud firewall allows port 80 inbound before debugging anything else; on most VPS providers this is a security-group rule, not an Nginx problem.

How does an Nginx configuration actually work?

Nginx config is a tree of contexts and directives. A simple directive is a name plus parameters ending in a semicolon (listen 80;); a block directive wraps child directives in braces (server { ... }). Blocks nest in a fixed order: main contains http, http contains server blocks, and server blocks contain location blocks. Everything after a # is a comment. The main file is /etc/nginx/nginx.conf, and on Debian/Ubuntu it ends with an include line that pulls in everything under /etc/nginx/sites-enabled/ — so each website gets its own small file, and enabling a site is just a symlink.

Three commands run your Nginx life: sudo nginx -t tests the configuration for syntax errors without touching the running server, sudo systemctl reload nginx re-reads the config gracefully (old workers finish their current requests, new workers pick up the new config — zero downtime), and sudo systemctl restart nginx does a full restart that drops in-flight connections. The iron rule of this guide, repeated in every section: test with nginx -t before every reload. A reload with a broken config fails and can leave the site down; a test catches the typo in a second.

How do I write my first server block?

An nginx server block (the Nginx name for a virtual host) tells Nginx which domain a request is for and where to find that site's files. Write it now — this is the file everything else in the guide builds on. Create the site directory and a test page:

sudo mkdir -p /var/www/example.com
echo '<h1>Hello from Nginx!</h1>' | sudo tee /var/www/example.com/index.html

Then create the server block file:

sudo nano /etc/nginx/sites-available/example.com
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.html;

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

Line by line: listen 80 answers plain HTTP (the second line adds IPv6), server_name says which hostnames this block handles, root sets the document directory, index names the file served for directory requests, and location / with try_files maps every request to a file on disk, returning a real 404 when nothing matches. Now enable it, test, and reload — in that order, every time:

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Visit http://example.com and you should see your "Hello from Nginx!" page. If nginx -t complains, read the error — it names the file and line number — fix it, and test again before reloading.

How do I serve static files with Nginx?

Nginx is fastest at exactly this: sending files from disk straight to browsers without involving any app code. The root directive does the mapping — for a request to /images/logo.png under location /images/ { root /data; }, Nginx serves /data/images/logo.png, appending the URI to the root path. When several location blocks match, Nginx picks the one with the longest prefix: location /images/ wins over location / for /images/foo.png, exactly as the official beginner's guide describes. That longest-prefix rule is worth memorizing now, because it is the source of half of all "why is the wrong content served" confusion.

Two additions make a static site production-shaped. First, a caching location for assets so browsers do not re-download them on every visit:

location ~* \.(css|js|png|jpg|jpeg|gif|ico|webp|woff2)$ {
    expires 30d;
    access_log off;
}

The ~* marks a case-insensitive regular expression that matches typical asset filenames, expires 30d tells browsers to cache them for a month, and access_log off keeps your logs from filling with asset hits. Second, keep the try_files $uri $uri/ =404; fallback so mistyped URLs get a proper 404 instead of leaking directory listings. A static site with this server block will out-perform most app stacks for raw throughput — Nginx was built for this job first.

How do I use Nginx as a reverse proxy?

A reverse proxy means Nginx accepts public requests and passes them to your real app running privately on the server. This nginx reverse proxy setup is the "just put Nginx in front of it" pattern from every Stack Overflow answer: your Node, Python, or PHP app listens on localhost:3000 where the internet cannot reach it, and Nginx faces the world on ports 80 and 443. The app never handles TLS, never sees the raw internet, and gets the request headers it needs. Replace the static location / with:

location / {
    proxy_pass http://localhost:3000;
    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;
}

Those four header lines are the part beginners skip and then regret. Host passes your domain through so the app knows which site was requested, X-Real-IP and X-Forwarded-For tell the app the visitor's real IP instead of 127.0.0.1, and X-Forwarded-Proto tells it the request arrived over HTTP or HTTPS — without it, apps build redirect loops or generate http:// links behind HTTPS. Then test and reload, as always: sudo nginx -t, sudo systemctl reload nginx.

Watch the proxy_pass trailing-slash trap, the most common proxy misconfiguration in the beginner field. With proxy_pass http://localhost:3000/; (trailing slash), the matched / prefix is stripped before forwarding; without it (proxy_pass http://localhost:3000;), the full URI goes through unchanged. For a plain location / both behave the same, but under a prefixed location like location /api/ the difference decides whether your backend sees /users or /api/users — and the wrong choice produces a 404 that looks like a backend bug but is a proxy bug. If your app runs in a container, read our Docker beginner guide — Nginx fronts the container exactly the way it fronts any localhost app here.

How do I add HTTPS with Certbot?

You add HTTPS with Certbot, the EFF's Let's Encrypt client, which gets a free certificate and rewrites your Nginx config to use it in one command. This nginx HTTPS Certbot flow has two prerequisites: your site must already answer on HTTP at port 80 (Certbot's Nginx plugin needs the existing server block, per the official instructions), and your domain must resolve to this server. Install Certbot with the Nginx plugin:

sudo apt install certbot python3-certbot-nginx -y

Then get the certificate and let Certbot edit the Nginx config automatically:

sudo certbot --nginx -d example.com -d www.example.com

Answer the prompts (email for renewal notices, agree to the terms), choose "redirect" when asked about HTTP→HTTPS — you almost always want it — and Certbot will add the listen 443 ssl server block, point at the new certificate files, and insert the redirect rule. Visit https://example.com and look for the lock icon. The official Certbot instructions confirm what makes this painless long-term: the package installs a cron job or systemd timer that renews certificates automatically before they expire, and you can prove renewal works without waiting with:

sudo certbot renew --dry-run

If your DNS or edge proxying lives at Cloudflare rather than on this server, pair this section with our Cloudflare beginner guide — the certificate setup there differs (Cloudflare can terminate TLS for you), and mixing the two approaches is a classic source of redirect loops.

What is a 502 Bad Gateway, and how do I fix it?

A 502 Bad Gateway means Nginx is fine but the app behind it is not answering — proxy_pass points at a backend that is down, not listening, or crashing. Your app on port 3000 stopped, the port changed, or it never started after a reboot. Nginx itself is working; the failure is upstream of the proxy, which is exactly why the error page says "Bad Gateway."

Fix it with this checklist, in order: (1) check the backend is actually listening — curl http://localhost:3000 should answer; if it does not, start or fix your app, not Nginx. (2) confirm the port in your proxy_pass matches the port the app binds. (3) read the error log — sudo tail -f /var/log/nginx/error.log — which will say "connection refused" or "connection timed out" for 502s and names the exact backend it tried. (4) check Nginx's own health with sudo systemctl status nginx and validate the config with sudo nginx -t before every reload. For a permanent answer to "is my site up right now," set up our Uptime Kuma beginner guide — it pages you the moment a backend goes dark so you fix it before your visitors notice.

Nginx vs Apache: which should a beginner pick?

For a beginner starting in 2026, Nginx is the default answer — here is the honest comparison so you know what you are choosing.

NginxApache
ArchitectureEvent-driven, async; few workers handle thousands of connectionsProcess/thread per connection (mpm_prefork, mpm_worker)
Static filesExceptionally fast — its original design goalFine, but heavier per connection under load
Reverse proxyFirst-class; the default front door for modern appsPossible via modules, less common as a pure proxy
ConfigurationCentral config files; reload to apply.htaccess per-directory overrides are its signature feature
Best for beginners whoserve static sites or proxy a Node/Python appneed shared-hosting-style per-directory rules or legacy PHP setups

The fair summary: Nginx's event-driven design wins on concurrency and static throughput, which is why it fronts so much of the modern web. Apache's .htaccess flexibility still matters on shared hosting and in legacy PHP shops — if your host gives you Apache with .htaccess, there is no reason to fight it. For your own VPS running the stack in this guide, Nginx is the simpler, faster choice.

How do I keep Nginx configs sane over time?

Three habits keep Nginx maintenance boring: one file per site in sites-available, nginx -t before every single reload (no exceptions — muscle memory, not judgment), and reading the logs when something changes. The logs live at /var/log/nginx/access.log and /var/log/nginx/error.log; the error log is the first place any mystery gets solved, and sudo nginx -T (capital T) dumps the entire effective configuration when you suspect an include is pulling in something you forgot. When server blocks multiply beyond what you can babysit by hand, our Ansible beginner guide shows how to template and roll out Nginx configs across servers without SSH-ing into each one.

Frequently asked questions

Is Nginx free?

Yes. Nginx is open-source software under a BSD-2-Clause-style license — free to download, use, and modify for any purpose, including commercial sites. The company behind it (now part of F5) sells a commercial product called Nginx Plus with enterprise features, but the open-source Nginx taught in this guide does everything a beginner needs, and it is what your distro's apt install nginx delivers.

Can I use Nginx on Windows or macOS?

Yes. The official download page ships a Windows build of every release (nginx/Windows-1.30.6 for the current stable), and macOS users can install it with Homebrew (brew install nginx). The config syntax is identical everywhere — this guide's commands are written for Ubuntu/Debian servers, which is where almost everyone runs Nginx in production, but your server blocks will transfer to other platforms as-is.

What is the difference between reload and restart?

reload re-reads the configuration and starts new worker processes while old workers gracefully finish their current requests — zero downtime, the right choice for config changes. restart stops and starts the whole Nginx process, dropping in-flight connections. Rule of thumb: sudo nginx -t then sudo systemctl reload nginx for config edits; reserve restart for upgrades or when reload misbehaves.

How many websites can one Nginx server host?

As many as the hardware comfortably handles — each site is just another server block file, and Nginx routes by server_name. Small sites can number in the dozens or hundreds on a single modest VPS; the practical limit is memory and traffic, not a config ceiling. One file per site in sites-available keeps that many-server life manageable.

How do I check which version of Nginx I have?

Run nginx -v — it prints the version to stderr, for example nginx version: nginx/1.30.6. Capital nginx -V adds the compile-time flags and installed modules, which is what you check when a directive from a tutorial "does not exist" (usually a missing module, not a missing version).

Sources

nginx.org — Beginner's Guide (official: master/worker processes, event-based model, config contexts, signals, proxy_pass, log locations); nginx.org — download page (mainline 1.31.8, stable 1.30.6, Windows builds — checked 2026-10-30). certbot.eff.org — Certbot instructions for Nginx (sudo certbot --nginx, automatic renewal via cron/systemd timer, certbot renew --dry-run — checked 2026-10-30). acornglobus.com — A Beginner's Guide to Nginx (static-site + reverse-proxy patterns, certbot --nginx flow). errin.dev — Getting Started with Nginx (sites-available/sites-enabled layout, nginx -t before reload, certbot --nginx -d example.com -d www.example.com). dev.to/oluwajubelo1 — Nginx for Beginners (apt install nginx, symlink enable, nginx -t, systemctl reload). dev.to/hemant_007 — Nginx Demystified (static serving + proxy_pass + the four proxy_set_header lines). dargslan.com — Nginx Beginner's Complete Guide (nginx -T, reload vs restart, HTTP→HTTPS redirect block, Let's Encrypt steps).

Featured on Twelve Tools Launch Llama Tools Launch your MCP