Install and Configure Nginx on Ubuntu 22.04: A Complete Web Server Guide from A to Z

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Get Nginx Running in 5 Minutes — Do First, Understand Later

Open a terminal and run these 3 commands:

sudo apt update
sudo apt install nginx -y
sudo systemctl start nginx

Done. Open a browser, type http://localhost or your server’s IP — the default Nginx page appears immediately. That’s all there is to it.

But stopping here means your web server can only serve the demo page. The rest of this guide explains why Nginx is so widely trusted and how to configure it to run a real website.

What Is Nginx and Why Not Use Apache?

Search “web server Linux” and you’ll see two names: Nginx and Apache. I’ve used both for years, and the honest answer is: it depends on the use case — but for modern servers, Nginx wins in most scenarios.

Apache was born in 1995 and handles each request with a dedicated thread. That’s fine for low traffic, but when you have 1,000 simultaneous connections, Apache needs 1,000 threads — and RAM evaporates fast.

Nginx was created in 2004 to solve exactly that problem. Its event-driven architecture allows a single process to handle thousands of connections at once. On a 4GB RAM server I manage, the Nginx worker process consumes around 10–15MB of RAM. Run Apache under the same traffic? It can easily balloon to 100MB+.

Nginx also does much more than just serve static files: reverse proxy, load balancer, HTTP cache — all in one lightweight package.

Installing Nginx on Ubuntu 22.04 the Right Way

Step 1: Update and Install

sudo apt update
sudo apt install nginx -y

Step 2: Check the Status

sudo systemctl status nginx

Seeing Active: active (running) highlighted in green means you’re good. If it hasn’t started automatically:

sudo systemctl start nginx
sudo systemctl enable nginx  # auto-start on reboot

Step 3: Open the Firewall

Ubuntu 22.04 uses UFW. Allow Nginx through:

sudo ufw allow 'Nginx Full'
sudo ufw status

Nginx Full opens both HTTP (port 80) and HTTPS (port 443). Only need HTTP for a test environment? Use Nginx HTTP instead.

Nginx Directory Structure — Know It So You Don’t Get Lost

Know where each file lives upfront, and you won’t waste time hunting for them later:

  • /etc/nginx/nginx.conf — the main configuration file, usually doesn’t need to be touched
  • /etc/nginx/sites-available/ — holds the config for each website (including disabled ones)
  • /etc/nginx/sites-enabled/ — symlinks to sites-available; only active sites live here
  • /var/www/html/ — the default web root directory
  • /var/log/nginx/ — log files (access.log and error.log)

This setup is quite clever: create a config in sites-available, then create a symlink to sites-enabled to enable it. Want to temporarily disable a site? Delete the symlink — no need to touch the original file. Very handy during maintenance.

Configuring Virtual Hosts — Hosting Multiple Websites on One Server

Many people skip this section and run into problems later. Virtual hosts allow you to run multiple websites on a single server, distinguished by their domain names.

Create a Directory and Content for Your Website

sudo mkdir -p /var/www/mysite.com/html
sudo chown -R $USER:$USER /var/www/mysite.com/html
echo "<h1>My Website</h1>" | sudo tee /var/www/mysite.com/html/index.html

Create the Nginx Configuration File

sudo nano /etc/nginx/sites-available/mysite.com

File contents:

server {
    listen 80;
    listen [::]:80;

    server_name mysite.com www.mysite.com;
    root /var/www/mysite.com/html;
    index index.html index.htm;

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

    access_log /var/log/nginx/mysite.access.log;
    error_log /var/log/nginx/mysite.error.log;
}

Enable the Site and Reload

sudo ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/
sudo nginx -t          # check syntax BEFORE reloading
sudo systemctl reload nginx

The nginx -t command is crucial — always run it before reloading to avoid crashing your web server due to a config syntax error.

Advanced: Nginx as a Reverse Proxy for Backend Applications

The most common production scenario: a Node.js/Python/Go application running on port 3000 that you want to expose externally on port 80/443 with a domain. Nginx sits in the middle as the bridge.

server {
    listen 80;
    server_name app.mysite.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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_cache_bypass $http_upgrade;
    }
}

The X-Real-IP and X-Forwarded-For headers let your backend application receive the client’s real IP address instead of Nginx’s IP. Without these lines, every request appears to come from 127.0.0.1 — making log debugging a real headache.

Enabling HTTPS with Let’s Encrypt — Free and Automated

Chrome marks every site without HTTPS as “Not Secure”, which impacts both UX and SEO rankings. Certbot handles this completely for free:

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d mysite.com -d www.mysite.com

Certbot automatically modifies your Nginx config to add SSL. Certificates renew automatically every 90 days — verify with:

sudo certbot renew --dry-run

Practical Tips from Daily Use

1. Logs Are Your Best Debugging Tool

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

Site having issues? Open 2 terminals: one watching error.log, one reproducing the problem. Most issues reveal themselves immediately.

2. Optimize Performance with Gzip and Basic Security

Add the following to the http {} block in /etc/nginx/nginx.conf:

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;

keepalive_timeout 65;
server_tokens off;  # hide Nginx version from response headers

gzip on compresses responses before sending them to the client — reducing bandwidth by 60–70% for HTML/CSS/JS files. server_tokens off hides the Nginx version from response headers, giving attackers less information to probe for vulnerabilities.

3. Rate Limiting — Block Spam and Brute Force

http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

    server {
        location /api/ {
            limit_req zone=api burst=20 nodelay;
        }
    }
}

This config limits each IP to 10 requests per second on /api/. A few lines of config blocks the majority of brute force attacks and basic request flooding — no additional tools required.

4. Zero-Downtime Reload — The Production Essential

# Check syntax
sudo nginx -t

# Reload with zero downtime
sudo systemctl reload nginx

Unlike restart, reload does not stop Nginx — it reads the new config and applies it without dropping existing connections. On production, always use reload instead of restart.

Wrapping Up

Nginx is actually less intimidating than its reputation suggests. Installation takes 2 minutes. Add another 5 and your virtual host is up and running. Reverse proxy or SSL — it’s just a matter of adding a few config blocks.

Three things to engrave in memory: always run nginx -t before reloading, use reload instead of restart, and never underestimate your log files — they’re the first place to look when something goes wrong.

Share: