Nginx Installation and Configuration on Linux: Complete Guide
Introduction
Nginx (pronounced "engine-x") is a high-performance, open-source web server, reverse proxy server, and load balancer. Originally created by Igor Sysoev in 2004 to solve the C10K problem (handling 10,000 concurrent connections), Nginx has become one of the most popular web servers worldwide, powering some of the largest and highest-traffic websites on the internet.
Unlike traditional web servers that use a process-per-request or thread-per-request model, Nginx uses an asynchronous, event-driven architecture. This design allows it to handle thousands of concurrent connections with minimal memory footprint and exceptional performance. Nginx excels at serving static content, acting as a reverse proxy, load balancing, caching, and handling SSL/TLS termination.
This comprehensive guide covers everything you need to know about installing, configuring, optimizing, and securing Nginx on various Linux distributions. Whether you're setting up a simple static website, a complex microservices architecture, or a high-traffic application, this guide provides the foundation you need for successful Nginx deployment.
Prerequisites
Before installing Nginx, ensure you have:
- A Linux server running Ubuntu 20.04/22.04/24.04, Debian 10/11/12, CentOS 7/8, Rocky Linux 8/9, or AlmaLinux 8/9
- Root access or a user account with sudo privileges
- A stable internet connection for package downloads
- Basic familiarity with the Linux command line
- Firewall access (ports 80 for HTTP and 443 for HTTPS)
- At least 512 MB RAM (1 GB+ recommended for production)
- Minimum 500 MB free disk space
- A registered domain name (optional, but recommended for production)
- Basic understanding of DNS configuration
- Text editor knowledge (nano, vim, or other)
Nginx Installation on Ubuntu and Debian
Installing Nginx on Debian-based systems is straightforward using the APT package manager.
Update Package Repository
Start by updating your package lists:
sudo apt update
sudo apt upgrade -y
Install Nginx
Install Nginx from the official repository:
sudo apt install nginx -y
This command installs Nginx along with all necessary dependencies and creates the nginx user and group automatically.
Install from Official Nginx Repository (Optional)
For the latest stable or mainline version, add the official Nginx repository:
# Install prerequisites
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring -y
# Import Nginx signing key
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
# Add repository for stable version
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
# Update and install
sudo apt update
sudo apt install nginx -y
Verify Installation
Check the installed version:
nginx -v
Expected output:
nginx version: nginx/1.24.0
Start and Enable Nginx
Start the Nginx service and enable it to launch on system boot:
sudo systemctl start nginx
sudo systemctl enable nginx
Check Service Status
Verify that Nginx is running:
sudo systemctl status nginx
You should see "active (running)" in the output.
Nginx Installation on CentOS, Rocky Linux, and AlmaLinux
Installing Nginx on Red Hat-based distributions follows a similar process:
Update Package Repository
Update your system packages:
sudo yum update -y # For CentOS 7
# OR
sudo dnf update -y # For CentOS 8, Rocky Linux, AlmaLinux
Install Nginx from EPEL Repository
Enable the EPEL repository first:
# CentOS 7
sudo yum install epel-release -y
# CentOS 8, Rocky Linux, AlmaLinux
sudo dnf install epel-release -y
Install Nginx:
# CentOS 7
sudo yum install nginx -y
# CentOS 8, Rocky Linux, AlmaLinux
sudo dnf install nginx -y
Install from Official Nginx Repository (Recommended)
For the latest version, use the official Nginx repository:
# Create repository file
sudo nano /etc/yum.repos.d/nginx.repo
Add the following content:
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
Install Nginx:
# CentOS 7
sudo yum install nginx -y
# CentOS 8, Rocky Linux, AlmaLinux
sudo dnf install nginx -y
Verify Installation
Check the version:
nginx -v
Start and Enable Nginx
Start and enable the service:
sudo systemctl start nginx
sudo systemctl enable nginx
Check Service Status
Verify the service is running:
sudo systemctl status nginx
Firewall Configuration
Configure your firewall to allow web traffic:
Ubuntu and Debian (UFW)
If using UFW:
# Allow HTTP and HTTPS
sudo ufw allow 'Nginx Full'
# Or allow specific ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable UFW if not already enabled
sudo ufw enable
# Check status
sudo ufw status
Nginx provides three UFW application profiles:
Nginx HTTP- Port 80 onlyNginx HTTPS- Port 443 onlyNginx Full- Both ports 80 and 443
CentOS, Rocky Linux, and AlmaLinux (firewalld)
If using firewalld:
# Allow HTTP and HTTPS services
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Or allow specific ports
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
# Reload firewall
sudo firewall-cmd --reload
# Verify rules
sudo firewall-cmd --list-all
Testing Nginx Installation
Verify that Nginx is serving content:
Test from Command Line
Use curl to test locally:
curl localhost
You should see HTML output from Nginx's default welcome page.
Test from Web Browser
Open a web browser and navigate to:
http://your_server_iphttp://your_domain.com(if DNS is configured)
You should see the "Welcome to nginx!" default page.
Check Nginx Process
Verify Nginx processes are running:
ps aux | grep nginx
You should see one master process and several worker processes.
Nginx Directory Structure
Understanding Nginx's file organization is essential:
Ubuntu and Debian Structure
/etc/nginx/
├── nginx.conf # Main configuration file
├── sites-available/ # Available server block configurations
├── sites-enabled/ # Enabled server blocks (symlinks)
├── conf.d/ # Additional configuration files
├── snippets/ # Configuration snippets
└── modules-enabled/ # Enabled modules
/var/www/html/ # Default document root
/var/log/nginx/ # Log files
├── access.log # Access logs
└── error.log # Error logs
/usr/share/nginx/html/ # Default web root (some installations)
CentOS, Rocky Linux, and AlmaLinux Structure
/etc/nginx/
├── nginx.conf # Main configuration file
├── conf.d/ # Additional configuration files (*.conf)
└── default.d/ # Default server configuration
/usr/share/nginx/html/ # Default document root
/var/log/nginx/ # Log files
├── access.log # Access logs
└── error.log # Error logs
Nginx Configuration Basics
Let's explore Nginx's configuration syntax and structure:
Main Configuration File
The main configuration file is /etc/nginx/nginx.conf. Here's a typical structure:
# User and worker processes
user nginx;
worker_processes auto;
# Error log location
error_log /var/log/nginx/error.log warn;
# PID file
pid /var/run/nginx.pid;
# Events block
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
# HTTP block
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging format
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Performance settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
# Include server blocks
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*; # Ubuntu/Debian
}
Understanding Configuration Contexts
Nginx configuration uses a hierarchical structure with contexts:
- Main context - Global settings
- Events context - Connection processing
- HTTP context - HTTP-specific settings
- Server context - Virtual host definitions
- Location context - URI-specific settings
Basic Directives Explained
# worker_processes: Number of worker processes (auto = CPU cores)
worker_processes auto;
# worker_connections: Max connections per worker
worker_connections 1024;
# sendfile: Efficient file transfer
sendfile on;
# tcp_nopush: Send headers in one packet
tcp_nopush on;
# tcp_nodelay: Disable Nagle algorithm for real-time connections
tcp_nodelay on;
# keepalive_timeout: Client connection keep-alive timeout
keepalive_timeout 65;
# client_max_body_size: Maximum upload size
client_max_body_size 20M;
Creating a Server Block (Virtual Host)
Server blocks allow hosting multiple websites on one server:
Ubuntu and Debian Server Block
Create a new server block configuration:
sudo nano /etc/nginx/sites-available/example.com
Add the following configuration:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;
access_log /var/log/nginx/example.com-access.log;
error_log /var/log/nginx/example.com-error.log;
location / {
try_files $uri $uri/ =404;
}
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Cache static files
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Create the document root:
sudo mkdir -p /var/www/example.com/html
Create a test index file:
sudo nano /var/www/example.com/html/index.html
Add:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to Example.com</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
text-align: center;
}
h1 { color: #009639; }
</style>
</head>
<body>
<h1>Success! Nginx is working!</h1>
<p>This is the example.com virtual host.</p>
</body>
</html>
Set proper permissions:
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com
Enable the server block by creating a symbolic link:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Test the configuration:
sudo nginx -t
Reload Nginx:
sudo systemctl reload nginx
CentOS, Rocky Linux, and AlmaLinux Server Block
Create a server block configuration:
sudo nano /etc/nginx/conf.d/example.com.conf
Add:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /usr/share/nginx/example.com/html;
index index.html index.htm;
access_log /var/log/nginx/example.com-access.log;
error_log /var/log/nginx/example.com-error.log;
location / {
try_files $uri $uri/ =404;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Create directories and files:
sudo mkdir -p /usr/share/nginx/example.com/html
sudo nano /usr/share/nginx/example.com/html/index.html
Set permissions:
sudo chown -R nginx:nginx /usr/share/nginx/example.com
sudo chmod -R 755 /usr/share/nginx/example.com
On SELinux-enabled systems:
sudo chcon -R -t httpd_sys_content_t /usr/share/nginx/example.com/
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
Advanced Location Block Configurations
Location blocks define how Nginx handles different URIs:
Exact Match
# Exact match - highest priority
location = /favicon.ico {
access_log off;
log_not_found off;
}
Prefix Match
# Prefix match
location /images/ {
root /var/www/example.com;
autoindex on;
}
Regular Expression Match
# Case-sensitive regex match
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
# Case-insensitive regex match
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 30d;
}
Preferential Prefix Match
# Preferential prefix match - higher priority than regex
location ^~ /static/ {
root /var/www;
}
Location Priority Order
- Exact match
= - Preferential prefix match
^~ - Regular expression match
~or~* - Prefix match (no modifier)
Performance Optimization
Optimize Nginx for maximum performance:
Worker Processes Configuration
# Set to number of CPU cores
worker_processes auto;
# Bind worker processes to CPU cores
worker_cpu_affinity auto;
# Maximum open files per worker
worker_rlimit_nofile 65535;
Connection Handling
events {
# Maximum connections per worker
worker_connections 4096;
# Use efficient connection method
use epoll; # Linux
# use kqueue; # FreeBSD/macOS
# Accept multiple connections at once
multi_accept on;
}
Buffer Size Tuning
http {
# Client body buffer size
client_body_buffer_size 128k;
# Client header buffer size
client_header_buffer_size 1k;
large_client_header_buffers 4 16k;
# Client body timeout
client_body_timeout 12;
client_header_timeout 12;
# Send timeout
send_timeout 10;
}
Keepalive Optimization
http {
# Keepalive timeout
keepalive_timeout 65;
# Maximum requests per connection
keepalive_requests 100;
}
Compression Configuration
http {
# Enable gzip compression
gzip on;
# Compression level (1-9)
gzip_comp_level 6;
# Minimum file size to compress
gzip_min_length 1000;
# Compress proxied requests
gzip_proxied any;
# File types to compress
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# Add Vary header
gzip_vary on;
# Disable for old IE
gzip_disable "msie6";
}
File Caching
http {
# Open file cache
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
}
FastCGI Cache (for PHP)
http {
# FastCGI cache path
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=FASTCGICACHE:100m
inactive=60m
max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
}
server {
location ~ \.php$ {
fastcgi_cache FASTCGICACHE;
fastcgi_cache_valid 200 60m;
add_header X-FastCGI-Cache $upstream_cache_status;
}
}
Security Hardening
Implement security best practices:
Hide Nginx Version
http {
# Hide version number
server_tokens off;
}
Security Headers
server {
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Prevent MIME type sniffing
add_header X-Content-Type-Options "nosniff" always;
# Enable XSS protection
add_header X-XSS-Protection "1; mode=block" always;
# Referrer policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
# HSTS (if using HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}
Limit Request Methods
server {
# Allow only GET, HEAD, POST
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 405;
}
}
Rate Limiting
http {
# Define rate limit zone
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
location / {
# Apply rate limit
limit_req zone=one burst=20 nodelay;
}
}
}
Connection Limiting
http {
# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /download/ {
limit_conn addr 5;
}
}
}
Restrict Access by IP
location /admin {
allow 192.168.1.100;
allow 10.0.0.0/24;
deny all;
}
Deny Access to Hidden Files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
Basic Authentication
# Install htpasswd utility
sudo apt install apache2-utils # Ubuntu/Debian
sudo yum install httpd-tools # CentOS/Rocky/AlmaLinux
# Create password file
sudo htpasswd -c /etc/nginx/.htpasswd username
Configure Nginx:
location /admin {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Disable Dangerous HTTP Methods
location / {
limit_except GET HEAD POST {
deny all;
}
}
SSL/TLS Configuration
Configure HTTPS for secure connections:
Generate Self-Signed Certificate (Testing Only)
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/nginx-selfsigned.key \
-out /etc/nginx/ssl/nginx-selfsigned.crt
HTTPS Server Block
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL certificates
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# SSL protocols and ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# SSL session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Root and index
root /var/www/example.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
Log Management
Configure effective logging:
Custom Log Format
http {
log_format custom '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log custom;
}
Conditional Logging
map $status $loggable {
~^[23] 0;
default 1;
}
server {
access_log /var/log/nginx/access.log combined if=$loggable;
}
Disable Logging for Specific Locations
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
View Real-Time Logs
# Access logs
sudo tail -f /var/log/nginx/access.log
# Error logs
sudo tail -f /var/log/nginx/error.log
# Both logs
sudo tail -f /var/log/nginx/*.log
Verification and Testing
Verify your Nginx configuration:
Test Configuration Syntax
sudo nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Reload Configuration
sudo systemctl reload nginx
# or
sudo nginx -s reload
Restart Nginx
sudo systemctl restart nginx
Check Nginx Status
sudo systemctl status nginx
View Active Connections
# Requires stub_status module
curl http://localhost/nginx_status
Configure stub_status:
server {
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
}
Troubleshooting Common Issues
Nginx Won't Start
Check error logs:
sudo journalctl -u nginx -n 50 --no-pager
sudo tail -f /var/log/nginx/error.log
Common causes:
- Configuration syntax errors
- Port already in use
- Permission issues
- Missing directories
Configuration Syntax Errors
Always test before reloading:
sudo nginx -t
Review the specific error message and line number.
Port Already in Use
Find what's using port 80:
sudo ss -tulpn | grep :80
sudo lsof -i :80
Permission Denied Errors
Check file ownership:
ls -la /var/www/example.com/
Fix permissions:
sudo chown -R www-data:www-data /var/www/example.com # Ubuntu/Debian
sudo chown -R nginx:nginx /usr/share/nginx/example.com # CentOS/Rocky
On SELinux systems:
# Check SELinux status
getenforce
# Set correct context
sudo chcon -R -t httpd_sys_content_t /usr/share/nginx/example.com/
# Set permanently
sudo semanage fcontext -a -t httpd_sys_content_t "/usr/share/nginx/example.com(/.*)?"
sudo restorecon -Rv /usr/share/nginx/example.com/
502 Bad Gateway
Common causes:
- PHP-FPM not running
- Wrong socket path
- Insufficient resources
Check PHP-FPM:
sudo systemctl status php8.1-fpm # Ubuntu/Debian
sudo systemctl status php-fpm # CentOS/Rocky
413 Request Entity Too Large
Increase upload size limit:
http {
client_max_body_size 20M;
}
Slow Performance
Check worker processes:
ps aux | grep nginx
Optimize worker_processes and worker_connections:
worker_processes auto;
events {
worker_connections 4096;
}
Best Practices
Follow these Nginx best practices:
Regular Updates
Keep Nginx updated:
# Ubuntu/Debian
sudo apt update
sudo apt upgrade nginx
# CentOS/Rocky/AlmaLinux
sudo dnf update nginx
Configuration Backups
Backup before making changes:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup-$(date +%F)
sudo tar -czf nginx-config-backup-$(date +%F).tar.gz /etc/nginx/
Use Include Directives
Organize configurations:
http {
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Separate Log Files per Site
server {
access_log /var/log/nginx/example.com-access.log;
error_log /var/log/nginx/example.com-error.log;
}
Monitor Resources
# Check memory usage
free -h
# Check CPU usage
top
# Check disk space
df -h
# Nginx processes
ps aux | grep nginx
Use Configuration Snippets
Create reusable snippets:
sudo nano /etc/nginx/snippets/ssl-params.conf
Add:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
Include in server blocks:
server {
listen 443 ssl http2;
include snippets/ssl-params.conf;
# ...
}
Conclusion
Nginx is a powerful, efficient, and flexible web server that excels in modern web hosting environments. This comprehensive guide has covered installation, configuration, optimization, and security hardening across multiple Linux distributions.
Key takeaways:
- Nginx's event-driven architecture provides superior performance for concurrent connections
- Proper server block configuration enables efficient multi-site hosting
- Performance tuning through worker processes and buffering significantly impacts efficiency
- Security hardening with headers, rate limiting, and SSL/TLS is essential for production
- Regular monitoring, updates, and backups maintain server health
Nginx's versatility extends beyond basic web serving to reverse proxying, load balancing, caching, and API gateway functionality. Continue exploring advanced features like HTTP/2, QUIC/HTTP/3 support, advanced caching strategies, and integration with modern application stacks.
For production deployments, always implement HTTPS, configure proper logging and monitoring, set up automated backups, and follow security best practices. The Nginx community and documentation provide excellent resources for advanced configurations and troubleshooting.


