DEV Community

Sospeter Mong'are
Sospeter Mong'are

Posted on

What Is Nginx? A Beginner's Guide to the Web Server Powering the Modern Internet

Introduction

If you have ever set up a website, deployed a web application, or rented a VPS (Virtual Private Server), you have almost certainly encountered the name Nginx (pronounced engine-x). It powers some of the busiest websites on the internet — including Netflix, Dropbox, and WordPress.com — yet many beginners find it intimidating at first glance.

This guide will change that.

By the end of this article, you will understand exactly what Nginx is, why it exists, how it works, and how to use it to serve real websites on a Linux server. No prior experience with web servers is required.


What Is Nginx?

Nginx is a web server — software that listens for requests coming from the internet and responds with web pages, files, or data.

But Nginx is more than just a web server. It is also:

  • A reverse proxy — it sits in front of your applications and forwards requests to them
  • A load balancer — it can distribute traffic across multiple servers
  • An HTTP cache — it can store and serve cached responses to reduce server load
  • An SSL terminator — it handles HTTPS encryption so your applications don't have to

Think of Nginx as the reception desk of a large office building. Every visitor (web request) walks in through the front door (port 80 or 443). The receptionist (Nginx) greets them, figures out where they need to go, and directs them to the right department — the Laravel team, the Node.js team, or the Python team. The visitor never wanders the building themselves.


Why Not Just Use Apache?

Apache is the other major web server and has been around since 1995. For years it dominated the web. So why does Nginx exist?

The answer comes down to architecture.

Apache creates a new thread or process for every incoming connection. This works fine for low traffic, but under heavy load — thousands of simultaneous connections — Apache consumes enormous amounts of RAM and CPU.

Nginx was built in 2004 specifically to solve this problem. It uses an event-driven, asynchronous architecture. Instead of one thread per connection, a single Nginx worker process can handle thousands of simultaneous connections efficiently using very little memory.

The result:

Apache Nginx
Architecture Thread per connection Event-driven
Memory usage under load High Low
Static file serving Good Excellent
Reverse proxy Possible Native, highly optimised
Configuration style .htaccess per directory Centralised config files

For modern application servers running Laravel, Node.js, or Python, Nginx is the industry standard choice.


How Nginx Fits Into a Real Server

Here is the architecture that professional developers use on production servers:

Internet
    |
    ▼
[ Nginx — Port 80/443 ]
    |
    ├──► example.com        → PHP-FPM (Laravel app)
    ├──► api.example.com    → Node.js running on port 3000
    └──► app.example.com    → Python running on port 8000
Enter fullscreen mode Exit fullscreen mode

Notice that none of the applications talk directly to the internet. Only Nginx does. This is the reverse proxy pattern and it is fundamental to how modern servers work.

Why is this pattern so powerful?

Security: Your Node.js app running on port 3000 is never exposed to the internet. UFW (the firewall) blocks port 3000 publicly. Only Nginx — on port 443 — is reachable, and Nginx decides what to forward.

SSL in one place: Instead of configuring HTTPS in your Laravel app, your Node.js app, and your Python app separately, you configure it once in Nginx. All apps automatically get HTTPS.

Multiple apps on one server: Without a reverse proxy, you could only run one application per server (one thing can listen on port 80 at a time). With Nginx routing by domain name, you can run dozens of applications on a single server.

Static files: Nginx serves static files (images, CSS, JavaScript) directly from disk at incredible speed — without involving PHP, Node.js, or Python at all. This dramatically reduces load on your application.


Understanding Nginx Configuration

Nginx configuration lives in /etc/nginx/ on Ubuntu/Debian systems. Here is the directory layout:

/etc/nginx/
├── nginx.conf              ← Main configuration file
├── sites-available/        ← All site configs (active or not)
│   ├── example.com
│   └── api.example.com
├── sites-enabled/          ← Symlinks to active sites only
│   ├── example.com → ../sites-available/example.com
│   └── api.example.com → ../sites-available/api.example.com
├── conf.d/                 ← Additional configuration fragments
└── snippets/               ← Reusable configuration pieces
Enter fullscreen mode Exit fullscreen mode

The sites-available and sites-enabled pattern

This is an elegant system. You write your site configuration in sites-available/. To activate it, you create a symbolic link (a shortcut) in sites-enabled/. To deactivate a site without deleting its configuration, you simply remove the symlink.

# Enable a site
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# Disable a site (config is preserved)
sudo rm /etc/nginx/sites-enabled/example.com
Enter fullscreen mode Exit fullscreen mode

Server Blocks — Nginx's Virtual Hosts

In Nginx, each website or application is configured using a server block. This is equivalent to Apache's Virtual Hosts. A server block tells Nginx: "When a request comes in for this domain name, here is how to handle it."

Example 1: Serving a Static HTML Website

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com/public;

    index index.html;

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

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;
}
Enter fullscreen mode Exit fullscreen mode

Let's break this down line by line:

  • listen 80 — listen for HTTP traffic on port 80
  • listen [::]:80 — also listen on IPv6
  • server_name example.com www.example.com — this block handles requests for these domain names
  • root /var/www/example.com/public — files are served from this directory
  • index index.html — the default file to serve when a directory is requested
  • try_files $uri $uri/ =404 — try to find the requested file; return 404 if not found
  • access_log and error_log — where to write logs for this site

Example 2: Serving a Laravel (PHP) Application

Laravel is a PHP framework that requires PHP-FPM to process .php files. Here is how Nginx is configured to work with it:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    root /var/www/example.com/public;

    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;
}
Enter fullscreen mode Exit fullscreen mode

The key addition here is the location ~ \.php$ block. This tells Nginx: "For any request ending in .php, don't serve it as a static file — pass it to PHP-FPM for processing."

PHP-FPM (FastCGI Process Manager) is a separate service that runs PHP code and returns the result to Nginx. They communicate via a Unix socket (php8.3-fpm.sock) — a fast, secure local communication channel.

The location ~ /\.(?!well-known).* block denies access to hidden files (files starting with a dot, like .env). This is a critical security rule — your .env file contains database passwords and application secrets, and it must never be publicly accessible.

Example 3: Reverse Proxy to Node.js

When your Node.js/Express application is running on port 3000, Nginx forwards requests to it like this:

server {
    listen 80;
    listen [::]:80;
    server_name api.example.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_set_header X-Forwarded-Proto $scheme;
    }

    access_log /var/log/nginx/api.example.com.access.log;
    error_log  /var/log/nginx/api.example.com.error.log;
}
Enter fullscreen mode Exit fullscreen mode

The proxy_pass http://localhost:3000 directive is the core of reverse proxying. Every request that hits api.example.com is forwarded to your Node.js process running locally on port 3000.

The proxy_set_header directives pass important information to your Node.js app — particularly X-Real-IP and X-Forwarded-For, which tell your application the real IP address of the visitor (since from Node.js's perspective, all requests appear to come from localhost).


Important Security Headers

Every Nginx server block should include these security headers:

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
Enter fullscreen mode Exit fullscreen mode
  • X-Frame-Options SAMEORIGIN — prevents your site from being embedded in an iframe on another website (protects against clickjacking attacks)
  • X-Content-Type-Options nosniff — prevents browsers from guessing the content type of a response (protects against MIME type confusion attacks)
  • X-XSS-Protection — enables the browser's built-in cross-site scripting filter

Hiding the Nginx Version Number

By default, Nginx tells the world exactly which version it is running. This is visible in HTTP response headers:

Server: nginx/1.24.0
Enter fullscreen mode Exit fullscreen mode

This is a gift to attackers — they can look up known vulnerabilities for that exact version. One line in your nginx.conf fixes this:

server_tokens off;
Enter fullscreen mode Exit fullscreen mode

After this, the header simply shows:

Server: nginx
Enter fullscreen mode Exit fullscreen mode

Testing and Reloading Configuration

This is the most important operational habit with Nginx. Always test your configuration before reloading.

# Test configuration syntax
sudo nginx -t

# If the test passes, reload gracefully
sudo systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

The difference between reload and restart is important:

  • reload — applies the new configuration without dropping existing connections. Zero downtime.
  • restart — stops and starts Nginx completely. Active connections are dropped.

Always use reload in production. Only use restart if Nginx is genuinely broken and needs a full restart.

A passing test looks like this:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Enter fullscreen mode Exit fullscreen mode

If you see any errors, fix them before reloading. A broken configuration with a running reload command will leave the old configuration running — Nginx is smart enough not to apply a broken config.


Understanding Nginx Worker Processes

When Nginx starts on a modern server, you will see multiple processes running:

nginx: master process
nginx: worker process
nginx: worker process
nginx: worker process
nginx: worker process
Enter fullscreen mode Exit fullscreen mode

The master process manages the workers and handles configuration reloads. It runs as root so it can bind to ports 80 and 443.

The worker processes handle the actual connections. They run as the www-data user — a low-privilege account — so even if a worker is compromised, the damage is limited.

By default on Ubuntu 24.04, Nginx automatically sets the number of workers to match your CPU core count. On a 4-core server you get 4 workers. This is the correct production setting.


Nginx Logs

Every site configured in Nginx writes to its own log files:

# Access log — every request
/var/log/nginx/example.com.access.log

# Error log — problems and warnings
/var/log/nginx/example.com.error.log
Enter fullscreen mode Exit fullscreen mode

To watch requests in real time:

sudo tail -f /var/log/nginx/example.com.access.log
Enter fullscreen mode Exit fullscreen mode

To watch errors:

sudo tail -f /var/log/nginx/example.com.error.log
Enter fullscreen mode Exit fullscreen mode

The access log shows you every request: the IP address, timestamp, requested URL, HTTP status code, and response size. The error log shows you configuration problems, upstream connection failures, and PHP-FPM errors.


Common Nginx Commands Reference

# Check if Nginx is running
sudo systemctl status nginx

# Start Nginx
sudo systemctl start nginx

# Stop Nginx
sudo systemctl stop nginx

# Reload configuration (zero downtime)
sudo systemctl reload nginx

# Test configuration syntax
sudo nginx -t

# Enable a site
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# Disable a site
sudo rm /etc/nginx/sites-enabled/example.com

# View access logs in real time
sudo tail -f /var/log/nginx/example.com.access.log

# View error logs in real time
sudo tail -f /var/log/nginx/example.com.error.log
Enter fullscreen mode Exit fullscreen mode

What Comes After Nginx?

Once Nginx is installed and configured, the typical next steps in building a production server are:

  1. PHP and PHP-FPM — to run Laravel and other PHP applications
  2. Node.js — to run Express.js APIs and applications
  3. SSL certificates with Let's Encrypt — to serve everything over HTTPS
  4. MySQL or PostgreSQL — for your application databases
  5. Redis — for caching and queues

All of these work behind Nginx. The internet sees only Nginx. Everything else is internal.


Summary

Nginx is the backbone of modern web server infrastructure. Here is what you have learned in this guide:

  • Nginx is a web server, reverse proxy, load balancer, and SSL terminator
  • It uses an event-driven architecture that handles thousands of connections efficiently
  • The reverse proxy pattern keeps your applications hidden from the internet
  • Server blocks define how Nginx handles requests for each domain
  • PHP applications use PHP-FPM, Node.js and Python apps use proxy_pass
  • Always test configuration with nginx -t before reloading
  • Use reload not restart in production to avoid downtime
  • Security headers and server_tokens off are essential hardening steps

Nginx rewards the time you invest in learning it. Once you understand its architecture, managing multiple applications on a single server becomes straightforward, predictable, and reliable.

Top comments (0)