Learn Nginx: Set Up a Modern, Scalable, High-Performance Web Server

Nginx is a powerful, high-performance web server that also functions well as a reverse proxy, load balancer, and HTTP cache. It is highly popular for serving static files, handling concurrent connections efficiently, and acting as a secure front end to application servers. In this guide, we will cover how to install, configure, and manage Nginx on Ubuntu Linux.

Why Use It #

Nginx is often chosen because of its:

  • Low memory usage and high concurrency.
  • Ability to serve static content very efficiently.
  • Robust reverse proxy and load balancing capabilities.
  • Simple yet powerful configuration files.
  • Widespread community support and documentation.

Nginx vs Apache #

Comparison:

Feature Nginx Apache
Architecture Event-driven, asynchronous Process/thread-based
Performance High concurrency, low memory Less efficient under heavy load
Static Content Very efficient Less efficient
Configuration Simple Complex, feature-rich
Load Balancing Built-in Requires third-party modules
Reverse Proxy Excellent Configurable with modules
OS Support Primarily Unix-based Cross-platform
Security Strong, minimal attack surface Larger attack surface
Resource Usage Low Higher under heavy load
Modules Limited Extensive
Popularity Increasing Well-established

Installation #

Platform: Debian / Ubuntu.

bash
sudo apt install nginx -y

Start:

bash
sudo systemctl start nginx

Auto start at boot:

bash
sudo systemctl enable nginx

Verify installation:

bash
nginx -v

Or visit: http://<public_ipv4>

Essential Commands #

1. Basics #

Start:

bash
sudo systemctl start nginx

Stop:

bash
sudo systemctl stop nginx

Restart:

bash
sudo systemctl restart nginx

Enable auto-start on boot:

bash
sudo systemctl enable nginx

Disable auto-start on boot:

bash
sudo systemctl disable nginx

Show status:

bash
sudo systemctl status nginx

2. Configuration #

Test:

Test the configuration for syntax errors before applying it.

bash
sudo nginx -t

If the configuration file has no errors, you’ll see output like:

bash
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload:

Reloading Nginx applies configuration changes without stopping the service or disrupting active connections.

bash
sudo systemctl reload nginx

Combine:

bash
sudo nginx -t && sudo systemctl reload nginx

3. Other #

Show version:

bash
nginx -v

With additional build configuration details:

bash
nginx -V

Show help:

bash
nginx -hs

Directory Structure #

Example:

text
/var/www/example.com/
  └── public_html/
      └── index.html
/etc/nginx/sites-available/
  └── example.com
/etc/nginx/sites-enabled/
  └── example.com -> ../sites-available/example.com (symlink)

Workflow #

Set Up a Site (HTML) #

List enabled sites:

bash
ls -l /etc/nginx/sites-enabled/

Disable default site:

This disables the default site by removing its active link, while keeping /etc/nginx/sites-available/default intact in case you want to re-enable it later.

bash
sudo unlink /etc/nginx/sites-enabled/default

Create directory:

bash
sudo mkdir -p /var/www/<name>/public_html

Example name: example.com

Add HTML file (for testing):

bash
sudo vim /var/www/<name>/public_html/index.html

Add:

/var/www/<name>/html/index.html
<h1>It works!</h1>

Set permissions:

bash
sudo chown -R www-data:www-data /var/www/

Create server block:

bash
sudo vim /etc/nginx/sites-available/<name>

Add:

/etc/nginx/sites-available/<name>
server {
    listen 80;
    listen [::]:80;

    server_name <public_ipv4> <domain> <www.domain>;

    root /var/www/<name>/public_html;
    index index.html;

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

Alternative (with HTTPS redirect):

/etc/nginx/sites-available/<name>
server {
    listen 80;
    listen [::]:80;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name <public_ipv4> <domain> <www.domain>;

    ssl_certificate <path/to/cert>.pem;
    ssl_certificate_key <path/to/key>.pem;

    root /var/www/<name>/public_html;
    index index.html;

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

Create symbolic link to enable site:

bash
sudo ln -s /etc/nginx/sites-available/<name> /etc/nginx/sites-enabled/

Test configuration:

bash
sudo nginx -t

If the output says the configuration is OK:

Reload NGINX:

bash
sudo systemctl restart nginx

Verify:

Visit website: http://<public_ipv4>

Set Up a Site (PHP) #

Install PHP:

bash
sudo apt install php-fpm -y

List enabled sites:

bash
ls -l /etc/nginx/sites-enabled/

Disable enabled sites:

bash
sudo unlink /etc/nginx/sites-enabled/<name>

Create directory:

bash
sudo mkdir -p /var/www/<name>/public_html

Example name: example.com

Add PHP file (for testing):

bash
sudo vim /var/www/<name>/public_html/index.php

Add:

/var/www/<name>/html/index.php
<?php phpinfo(); ?>

Set permissions:

bash
sudo chown -R www-data:www-data /var/www/

Create server block:

bash
sudo vim /etc/nginx/sites-available/<name>

Add:

/etc/nginx/sites-available/<name>
server {
  listen 80;
  listen [::]:80;

  server_name <public_ipv4> <domain>;

  root /var/www/<name>/public_html;
  index index.php;

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

  location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php<version>-fpm.sock;
  }
}

Create symbolic link to enable site:

bash
sudo ln -s /etc/nginx/sites-available/<name> /etc/nginx/sites-enabled/

Test configuration:

bash
sudo nginx -t

If the output says the configuration is OK:

Reload NGINX:

bash
sudo systemctl restart nginx

Verify:

Visit website: http://<public_ipv4>

Monitoring and Logs #

Install CCZE (optional):

CCZE is a tool that shows logs with colours to make them easier to read.

bash
sudo apt install ccze -y

View access log:

bash
sudo tail -f /var/log/nginx/access.log | ccze

View error log:

bash
sudo tail -f /var/log/nginx/error.log | ccze

Tips and Tricks #

Use Include Statements for Reusability #

Put shared logic in snippets:

text
/etc/nginx/snippets/ssl-params.conf
/etc/nginx/snippets/letsencrypt.conf
/etc/nginx/sites-available/<name>
include snippets/ssl-params.conf;

Troubleshooting #

Unable to Connect #

Symptoms:

  • Browser shows “Unable to connect” or similar error.

Causes:

  • No site configuration is linked in /etc/nginx/sites-enabled/.
  • NGINX is running but not serving any site.
  • Server block missing or misconfigured listen directive.

Solutions:

  • Check for active site configurations:
bash
ls -l /etc/nginx/sites-enabled/
  • Link a site config:
bash
sudo ln -s /etc/nginx/sites-available/<name> /etc/nginx/sites-enabled/
  • Validate configuration:
bash
sudo nginx -t
  • Reload NGINX:
bash
sudo systemctl reload nginx
  • Ensure your site config has a valid listen directive:
/etc/nginx/sites-available/<name>
server {
    listen 80;

    server_name <public_ipv4> <domain>;

    root /var/www/<name>;
    index index.html;
}

403 Forbidden #

Symptoms:

  • Browser shows a "403 Forbidden" error when trying to access a site.

Causes:

  • Incorrect file or directory permissions.
  • NGINX not having read access to the web root.
  • Missing index directive in the configuration.

Solutions:

  • Ensure correct permissions:
bash
sudo chown -R www-data:www-data /var/www/
sudo chmod -R 755 /var/www/
  • Add or correct the index directive in your server block:
/etc/nginx/sites-available/<name>
index index.html;

502 Bad Gateway #

Symptoms:

  • NGINX returns a "502 Bad Gateway".

Causes:

  • The upstream service (e.g., PHP-FPM, Node.js, etc.) is not running or not reachable.
  • Incorrect fastcgi_pass setting.

Solutions:

  • Ensure the upstream service is running:
bash
sudo systemctl status php<version>-fpm
  • Check the socket in your config:
/etc/nginx/sites-available/<domain>
fastcgi_pass unix:/run/php/php<version>-fpm.sock;

Too Many Redirects #

Symptoms:

  • Browser shows “ERR_TOO_MANY_REDIRECTS”

Causes:

  • Misconfigured return or rewrite rules
  • Conflicting HTTP-to-HTTPS redirection loops

Solutions:

  • Ensure HTTPS and HTTP configs are separate:
bash
server {
    listen 80;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    ...
}
  • Double-check server blocks for conflicting redirections

SSL/TLS Certificate Errors #

Symptoms:

  • Browser shows "Your connection is not private" or similar TLS errors.

Causes:

  • Misconfigured SSL settings
  • Expired or invalid certificate
  • Missing certificate files

Solutions:

  • Check certificate expiry:
bash
openssl x509 -enddate -noout -in /etc/ssl/certs/your_cert.pem
  • Ensure proper paths and permissions:
bash
ssl_certificate <path/to/cert>.pem;
ssl_certificate_key <path/to/key>.pem;
  • Restart NGINX after changes:
bash
sudo systemctl restart nginx