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 widely adopted for its performance, efficiency, and versatility in web infrastructure. Key reasons for using NGINX include:

  • Low resource usage with high concurrency
    NGINX uses an event-driven, asynchronous architecture that handles thousands of simultaneous connections with minimal memory and CPU consumption. This makes it ideal for high-traffic environments.
  • Efficient static content delivery
    It serves static files such as HTML, CSS, JavaScript, and images with exceptional speed, reducing server load and improving client response times.
  • Advanced reverse proxy and load balancing
    NGINX supports multiple load balancing methods (round-robin, IP-hash, least connections) and can proxy requests to backend servers, enabling horizontal scaling and high availability.
  • Clear and flexible configuration
    Its configuration syntax is declarative and structured, making it easy to read, manage, and automate. Features like conditional blocks, variable usage, and modular design allow complex setups with minimal overhead.
  • Extensive community and documentation
    As a mature and popular open-source project, NGINX benefits from a large community, frequent updates, and comprehensive documentation, facilitating troubleshooting and continuous learning.

NGINX vs Apache #

NGINX and Apache are two of the most widely used web servers. Each has its strengths, but they differ significantly in architecture and performance characteristics.

Comparison Table

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, declarative Complex, feature-rich
Load Balancing Built-in support Requires third-party modules
Reverse Proxy First-class support Configurable via modules
OS Support Primarily Unix-based Cross-platform
Security Strong, minimal attack surface Larger attack surface
Resource Usage Low, even under high load Higher memory and CPU usage
Modules Limited, lightweight Extensive and mature
Popularity Rapidly growing Long-standing, widely used

Key Takeaways

  • NGINX offers superior performance and scalability, especially in high-concurrency scenarios.
  • Apache is more flexible and extensible through its mature module ecosystem.

NGINX is the preferred choice for modern, containerised, or microservice-based architectures.

Installation #

Install on Linux (Ubuntu/Debian):

bash
sudo apt install nginx -y

Start NGINX service:

bash
sudo systemctl start nginx

Enable NGINX to start at boot:

bash
sudo systemctl enable nginx

Verify installation:

bash
nginx -v

Or open a browser and visit:

text
http://<public_ipv4>

Essential Commands #

1. Basics #

Start NGINX:

bash
sudo systemctl start nginx

Stop NGINX:

bash
sudo systemctl stop nginx

Restart NGINX:

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 service status:

bash
sudo systemctl status nginx

2. Configuration #

Test configuration:

bash
sudo nginx -t

Expected output:

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

Before applying changes, validate the NGINX configuration for syntax errors.

Reload NGINX:

bash
sudo systemctl reload nginx

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

Combine test and reload:

bash
sudo nginx -t && sudo systemctl reload nginx

3. Other #

Show NGINX version:

bash
nginx -v

With additional build configuration details:

bash
nginx -V

Show help:

bash
nginx -h

Directory Structure #

Example:

filesystem
/var/www/example.com/
  └── public_html/
      └── index.html

/var/www/staging.example.com/
  └── public_html/
      └── index.html

/etc/nginx/sites-available/
  ├── example.com
  └── staging.example.com

/etc/nginx/sites-enabled/
  ├── example.com -> ../sites-available/example.com (symlink)
  └── staging.example.com -> ../sites-available/staging.example.com (symlink)

Website Files

  • /var/www/example.com/public_html/index.html
    Contains the website files for example.com. The public_html folder holds all the publicly accessible content.
  • /var/www/staging.example.com/public_html/index.html
    Contains the website files for staging.example.com, a separate site or environment, with its own public_html folder.

NGINX Configuration

  • /etc/nginx/sites-available/
    This directory stores all available site configurations. Each file corresponds to a website or virtual host (e.g., example.com, staging.example.com).
  • /etc/nginx/sites-enabled/
    This directory contains symbolic links (symlinks) to the configuration files in sites-available for sites that are currently active. For example: example.com -> ../sites-available/example.com means the example.com configuration is enabled and used by NGINX.

Summary

  • Website files are kept in /var/www/<site>/public_html/.
  • Site configurations are stored in /etc/nginx/sites-available/.
  • Active sites are enabled by symlinking their config files into /etc/nginx/sites-enabled/.
  • This separation makes managing multiple sites simple and prevents accidental removal of configurations.

Workflow #

Set Up a Site (HTML) #

List enabled sites:

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

Disable default site:

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

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.

Create site directory:

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

Example site: example.com

Add test HTML file:

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

Add:

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

Set ownership:

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

Create server block configuration:

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

Add:

/etc/nginx/sites-available/<site>
server {
  listen 80;
  listen [::]:80;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

  root /var/www/<site>/public_html;
  index index.html; # Serve index.html when a directory is requested

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

Or with HTTP to HTTPS redirect:

/etc/nginx/sites-available/<site>
server {
  listen 80;
  listen [::]:80;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

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

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

  root /var/www/<site>/public_html;
  index index.html; # Serve index.html when a directory is requested

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

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

Create a symbolic link to enable the site:

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

Test configuration:

bash
sudo nginx -t

Expected output:

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

Reload NGINX:

bash
sudo systemctl reload nginx

To verify, visit the site in a browser:

text
http://<public_ipv4>

Set Up a Site (PHP) #

Install PHP-FPM:

bash
sudo apt install php-fpm -y

List enabled sites:

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

Disable default site:

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

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.

Create site directory:

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

Example site: example.com

Add PHP test file:

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

Add:

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

Set ownership:

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

Create server block configuration:

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

Add:

/etc/nginx/sites-available/<site>
server {
  listen 80;
  listen [::]:80;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

  root /var/www/<site>/public_html;
  index index.php; # Serve index.php when a directory is requested

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

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

Or with HTTP to HTTPS redirect:

/etc/nginx/sites-available/<site>
server {
  listen 80;
  listen [::]:80;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

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

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name <name> ; # Add: <domain>, www.<domain> or <public_ipv4>

  root /var/www/<site>/public_html;
  index index.php; # Serve index.php when a directory is requested

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

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

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

Create symbolic link to enable site:

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

Test configuration:

bash
sudo nginx -t

Expected output:

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

Reload NGINX:

bash
sudo systemctl reload nginx

To verify, visit the site in a browser:

text
http://<public_ipv4>

Monitoring and Logs #

Install CCZE (optional):

bash
sudo apt install ccze -y

CCZE enhances log readability by adding colour to the output.

View NGINX access log:

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

View NGINX error log:

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

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/<site> /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/<site>
server {
  listen 80;
  listen [::]:80;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

  root /var/www/<site>/public_html;
  index index.html; # Serve index.html when a directory is requested
}

403 Forbidden #

Symptoms:

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

Causes:

  • Incorrect file or directory permissions
    Files or directories do not have the correct ownership or mode.
  • NGINX does not have read access to the web root
    The web server user (typically www-data) cannot access the site directory or files.
  • Missing or misconfigured index directive
    NGINX cannot find a default file to serve when a directory is requested.

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/<site>
index index.html; # Use index.php if your site uses PHP
  • Or to support both:
/etc/nginx/sites-available/<site>
index index.html index.php;

502 Bad Gateway #

Symptoms:

Nginx returns a 502 Bad Gateway error.

Cause:

The upstream server (e.g. PHP-FPM) is down, misconfigured, or unreachable.

Solution:

1. Check Upstream Server Status

Ensure the backend service (e.g. PHP-FPM) is running and listening on the correct socket or port:

bash
sudo systemctl status php<version>-fpm

2. Verify Nginx Configuration

Ensure Nginx is properly configured to pass requests to the correct upstream socket or port:

/etc/nginx/sites-available/<site>
location ~ \.php$ {
  include snippets/fastcgi-php.conf;
  fastcgi_pass unix:/run/php/php<version>-fpm.sock;   # Confirm the PHP version matches the installed version
}

3. Check Logs

Inspect log files for error details:

bash
# Nginx error log
tail -f /var/log/nginx/error.log

# PHP-FPM log
tail -f /var/log/php<version>-fpm.log

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:
/etc/nginx/sites-available/<site>
server {
  listen 80;
  listen [::]:80;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>

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

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name <name>; # Add: <domain>, www.<domain> or <public_ipv4>
  
  ...
}
  • 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
sudo openssl x509 -enddate -noout -in /etc/ssl/cloudflare/cert.pem
  • Ensure proper paths and permissions:
/etc/nginx/sites-available/<site>
ssl_certificate /etc/ssl/cloudflare/cert.pem;
ssl_certificate_key /etc/ssl/cloudflare/key.pem;
  • Restart NGINX after changes:
bash
sudo systemctl restart nginx