Your website loads slowly. Traffic spikes crash your server. You need a solution that handles thousands of visitors without breaking a sweat.
NGINX solves these problems. It is a high-performance web server that also works as a reverse proxy, load balancer, and HTTP cache. Whether you run a personal blog or a business application, NGINX delivers speed and reliability while using minimal server resources.
This guide teaches you NGINX configuration from scratch with practical configurations you can use immediately.
What is Nginx and why use it?
NGINX (pronounced engine x) is a web server that handles high traffic efficiently. Unlike traditional servers that create a new thread for each visitor, NGINX uses an event-driven model. One worker process handles multiple connections simultaneously without consuming excessive memory.
Apache creates a separate worker for each visitor. With 10,000 visitors, you need 10,000 workers. NGINX uses a handful of workers that juggle all connections. The difference shows in your server bill and site speed.
NGINX does four main jobs. It serves static files like HTML, CSS, images, and JavaScript directly to browsers. It acts as a reverse proxy by forwarding requests to backend applications. It balances traffic across multiple servers to prevent overload. It caches responses to reduce load on your backend systems.
The configuration is straightforward once you understand the basics.
Understanding NGINX directives
Directives are instructions that tell NGINX what to do. Every directive follows this format:
“`nginx title="Show the syntax of an Nginx directive" directive_name value;
The semicolon is not optional. Forget it, and NGINX refuses to start.
Directives live in different contexts. The main context affects your entire NGINX installation. The HTTP block contains web server settings. Server blocks define individual websites. Location blocks handle specific URL patterns.
More specific directives override general ones. A location directive beats a server directive. This hierarchy lets you set defaults and override them where needed.
```nginx title="Disable gzip for /api/ while keeping it globally enabled"
http {
gzip on;
server {
listen 80;
server_name example.com;
location /api/ {
gzip off;
}
}
}
Here, gzip is enabled globally but disabled for /api/ URLs. Location overrides HTTP.
Configuration file hierarchy
NGINX organizes configuration in a clear hierarchy. Understanding this structure prevents conflicts.
The main context sits at the top. Define the user NGINX runs as, worker processes, and error logging here.
“`nginx title="Configure Nginx workers logging and PID file" user www-data; workerprocesses auto; errorlog /var/log/nginx/error.log warn; pid /var/run/nginx.pid;
The worker_processes auto directive creates one worker per CPU core.
The events block controls connection handling.
```nginx title="Allow 1024 connections per worker"
events {
worker_connections 1024;
}
Each worker process handles 1,024 simultaneous connections. On a four-core server with auto workers, you handle 4,096 concurrent visitors.
The HTTP block contains all web server configuration.
“`nginx title="Serve example.com from /var/www/html" http { include /etc/nginx/mime.types; defaulttype application/octet-stream; server { listen 80; servername example.com; location / { root /var/www/html; } } }
Server blocks define virtual hosts. Location blocks handle specific URI patterns within a website.
Split configuration across files for easier management.
```nginx title="Include conf.d and enabled site configurations"
http {
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
This keeps each site in its own file. Enable or disable sites without editing the main configuration.
HTTP block essentials
The HTTP block holds global settings that apply to all websites unless overridden in server blocks.
Start with performance settings.
“`nginx title="Enable efficient HTTP connection handling" http { sendfile on; tcpnopush on; tcpnodelay on; keepalivetimeout 65; typeshashmaxsize 2048; }
The sendfile directive uses your kernel to send files directly, improving performance.
Enable compression to reduce bandwidth.
```nginx title="Compress selected text and application response types"
http {
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss;
}
Level 6 balances CPU usage and compression. Do not compress already compressed files like images, videos, or PDFs.
Add security headers.
“`nginx title="Hide the Nginx version and add security headers" http { servertokens off; addheader X-Frame-Options "SAMEORIGIN" always; addheader X-Content-Type-Options "nosniff" always; addheader X-XSS-Protection "1; mode=block" always; }
The server_tokens directive hides your NGINX version number from error pages and headers.
### Server blocks and domain configuration
Server blocks define how NGINX handles different domains. Each server block configures one website.
Here is a basic HTTP server.
```nginx title="Serve example.com over IPv4 and IPv6 HTTP"
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
The listen directive specifies the port. Double colon syntax enables IPv6. Production servers should handle both IPv4 and IPv6.
For production, use HTTPS with HTTP/2.
“`nginx title="Serve example.com over HTTP/2 TLS" server { listen 443 ssl http2; listen [::]:443 ssl http2; servername example.com www.example.com; sslcertificate /etc/nginx/ssl/example.com.crt; sslcertificatekey /etc/nginx/ssl/example.com.key; sslprotocols TLSv1.2 TLSv1.3; sslsessioncache shared:SSL:50m; addheader Strict-Transport-Security "max-age=31536000" always; root /var/www/example.com; index index.html; }
HTTP/2 allows multiplexing multiple requests over a single connection. The ssl_certificate should include your certificate chain. The Strict-Transport-Security header tells browsers to always use HTTPS for one year.
Redirect HTTP to HTTPS automatically.
```nginx title="Redirect example.com from HTTP to HTTPS"
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
The 301 status code indicates a permanent redirect.
Canonicalize your domain. Choose www or non-www, then redirect the other.
“`nginx title="Redirect www.example.com to the apex domain" server { listen 443 ssl http2; servername www.example.com; return 301 https://example.com$requesturi; }
To redirect non-www to www instead, reverse the server_name values.
The server_name directive accepts multiple domains separated by spaces. Wildcards work at the start or end.
```nginx title="Match every example.com subdomain"
server_name *.example.com;
This matches any subdomain like blog.example.com or api.example.com.
When multiple server blocks could match a request, NGINX follows priority. Exact matches win, then longest wildcard, then first matching regular expression.
Make sure your firewall allows traffic.
“`bash title="Allow HTTP and HTTPS through UFW" sudo ufw allow 80/tcp sudo ufw allow 443/tcp
Without open ports, visitors cannot reach your site.
### Location blocks for path management
Location blocks define how NGINX handles specific URIs. NGINX processes location blocks in priority order.
Exact matches come first.
```nginx title="Suppress logging for exactly /favicon.ico"
location = /favicon.ico {
access_log off;
log_not_found off;
}
This matches only /favicon.ico exactly. It is the fastest match type.
Prefix matches without modifiers come next.
“`nginx title="Cache files under /images/ for 30 days" location /images/ { root /var/www/static; expires 30d; }
This matches any URI starting with /images/.
Priority prefix matches stop further searching.
```nginx title="Proxy /api/ requests without regex matching"
location ^~ /api/ {
proxy_pass http://backend_api;
}
If this prefix matches, NGINX skips regex checking.
Case sensitive regex matches use tilde.
“`nginx title="Send PHP requests to PHP-FPM" location ~ .php$ { fastcgipass unix:/var/run/php-fpm.sock; include fastcgiparams; }
Case insensitive regex uses tilde asterisk.
```nginx title="Cache image assets for one year"
location ~* .(jpg|jpeg|png|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
Understanding root versus alias matters. With root, the location path gets appended.
“`nginx title="Map /static/ requests beneath /var/www/data" location /static/ { root /var/www/data; }
A request to /static/style.css looks for /var/www/data/static/style.css.
With an alias, the location path gets replaced.
```nginx title="Replace /static/ with the /var/www/files/ alias"
location /static/ {
alias /var/www/files/;
}
A request to /static/style.css looks for /var/www/files/style.css. Notice the trailing slash.
The try_files directive checks files in order.
“`nginx title="Serve existing files or return 404" location / { try_files $uri $uri/ =404; }
First tries the exact URI as a file, then as a directory, then returns 404.
### Reverse proxy basics
NGINX excels at forwarding requests to backend applications. Define backend servers in an upstream block.
```nginx title="Proxy example.com to a backend app"
upstream backend_app {
server 192.168.1.10:3000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_app;
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;
}
}
The proxy_pass directive sends requests to your backend. Setting headers ensures the backend receives correct client information.
For load balancing across multiple servers, list them all.
“`nginx title="Load balance requests across three backend servers" upstream backend_app { server 192.168.1.10:3000; server 192.168.1.11:3000; server 192.168.1.12:3000; }
NGINX distributes requests evenly using round robin. Add health checks so NGINX stops routing to failed servers.
```nginx title="Mark a backend unavailable after repeated failures"
upstream backend_app {
server 192.168.1.10:3000 max_fails=3 fail_timeout=30s;
}
After three failed attempts, NGINX marks the server down for 30 seconds.
Testing and troubleshooting
When things go wrong, check logs first. Error log lives at /var/log/nginx/error.log. Access log is at /var/log/nginx/access.log.
Always test configuration before reloading.
“`bash title="Test the Nginx configuration" sudo nginx -t
This validates syntax and configuration. Fix any errors before proceeding.
Reload configuration without dropping connections.
```bash title="Reload Nginx without dropping connections"
sudo nginx -s reload
NGINX starts new workers with updated configuration, then gracefully shuts down old workers.
Check which version you run.
“`bash title="Show the installed Nginx version" nginx -v
For compile options and modules, use capital V.
```bash title="Show Nginx version modules and build options"
nginx -V
If changes do not take effect, verify you edited the correct file and reloaded NGINX. Check for syntax errors. Look for conflicting directives in different files.
If you get 413 Request Entity Too Large errors, increase the limit.
“`nginx title="Allow request bodies up to 50 MiB" clientmaxbody_size 50M;
If you see client IPs as 127.0.0.1 behind a load balancer, configure the real IP module.
```nginx title="Trust forwarded client IPs from a private subnet"
set_real_ip_from 192.168.1.0/24;
real_ip_header X-Forwarded-For;
Quick start on your server
Installing NGINX on Ubuntu or Debian is straightforward.
“`bash title="Install Nginx on Ubuntu or Debian" sudo apt update sudo apt install nginx
Start NGINX and enable it to run on boot.
```bash title="Start Nginx and enable it at boot"
sudo systemctl start nginx
sudo systemctl enable nginx
Verify NGINX runs.
“`bash title="Show the Nginx service status" sudo systemctl status nginx
The default configuration serves a welcome page. Visit your server IP to confirm.
Create a basic configuration for your site in /etc/nginx/sites-available/yourdomain.com.
```nginx title="Serve yourdomain.com from its document root"
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Enable the site by creating a symbolic link to sites-enabled. This links your configuration file without copying it.
“`bash title="Enable the yourdomain.com site configuration" sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
Test and reload.
```bash title="Create and assign a Bitwarden backup directory"
sudo mkdir -p /opt/bitwarden/backups
sudo chown bitwarden:bitwarden /opt/bitwarden/backups
For HTTPS, use Let's Encrypt with Certbot for free SSL certificates.
“`bash title="Install Certbot and request a certificate for yourdomain.com" sudo apt install certbot python3-certbot-nginx sudo certbot –nginx -d yourdomain.com
Certbot automatically configures NGINX for HTTPS and sets up certificate renewal.
### Common troubleshooting scenarios
#### Nginx fails to start after a configuration change
Run `nginx -t` to identify the syntax error. The output shows the file and line number. Common causes include missing semicolons, unclosed braces, or invalid directive names.
```bash title="Test Nginx after a configuration change"
sudo nginx -t
nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/example.com:15
The error points to line 15. Check for missing semicolons on previous lines.
Port 80 or 443 already in use
Another process occupies the port. Find what is using it:
“`bash title="Find processes listening on ports 80 and 443" sudo lsof -i :80 sudo lsof -i :443
Common culprits include Apache running alongside NGINX. Stop the conflicting service or change NGINX to listen on different ports.
#### Permission denied errors
NGINX cannot access files or directories. Check file permissions:
```bash title="Inspect permissions for the example.com document root"
ls -la /var/www/example.com
NGINX runs as the www-data user by default. Ensure www-data can read files and access directories:
Safety check: Confirm the target and keep a recent backup or snapshot. Preview the affected resources where possible, and document a tested recovery or rollback path before running this command.
“`bash title="Assign the example.com document root to www-data" sudo chown -R www-data:www-data /var/www/example.com sudo chmod -R 755 /var/www/example.com
#### SSL certificate errors
Certificate path incorrect or certificate expired. Verify certificate files exist:
```bash title="List files in the Nginx SSL directory"
sudo ls -la /etc/nginx/ssl/
Check certificate expiration:
“`bash title="Show the example.com certificate validity dates" sudo openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -dates
For Let's Encrypt certificates, check Certbot logs:
```bash title="Follow the Certbot log"
sudo tail -f /var/log/letsencrypt/letsencrypt.log
502 bad gateway with upstream services
Backend application not running or unreachable. Verify backend listens on the configured port:
“`bash title="Find the process listening on port 3000" sudo netstat -tlnp | grep 3000
Check application logs for errors. Verify that the proxy_pass URL matches the backend address and port exactly.
Test backend connectivity:
```bash title="Test the backend on localhost port 3000"
curl
504 gateway timeout
Backend responds too slowly. Increase proxy timeouts:
“`nginx title="Proxy requests with 60-second backend timeouts" location / { proxypass http://backend; proxyconnecttimeout 60s; proxysendtimeout 60s; proxyread_timeout 60s; }
Investigate why the backend is slow. Check backend application logs and server resources.
#### Worker process crashes
Check error logs for segmentation faults or core dumps:
```bash title="Show the last 100 Nginx error-log lines"
sudo tail -100 /var/log/nginx/error.log
Common causes include third-party modules with bugs, insufficient system resources, or corrupted configurations. Disable recently added modules to isolate the issue.
High memory usage
Check worker process memory:
“`bash title="List Nginx processes and memory usage" ps aux | grep nginx
Large SSL session caches consume memory. Reduce cache size:
```nginx title="Reduce the shared SSL session cache to 10 MiB"
ssl_session_cache shared:SSL:10m;
Reduce worker_connections if memory is constrained:
“`nginx title="Limit each Nginx worker to 512 connections" events { worker_connections 512; }
#### Logs not rotating
Log files grow too large. Verify logrotate configuration:
```bash title="Show the Nginx logrotate configuration"
sudo cat /etc/logrotate.d/nginx
Manually rotate logs:
“`bash title="Force an immediate Nginx log rotation" sudo logrotate -f /etc/logrotate.d/nginx
#### DNS resolution fails for upstream servers
NGINX cannot resolve backend hostnames. Add resolver directive:
```nginx title="Resolve upstream hostnames with Google Public DNS"
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
Or use IP addresses in upstream blocks instead of hostnames.
Cannot bind to a privileged port (below 1024)
NGINX must run as root to bind to ports 80 and 443. Check NGINX starts with sudo or as a system service. The master process runs as root, worker processes run as www-data.
Verify systemd service configuration:
“`bash title="Show the Nginx systemd unit configuration" sudo systemctl cat nginx
### Common questions
#### How do I host multiple websites on one server?
Create a server block for each domain in /etc/nginx/sites-available/. Give each its own server_name and root directory. Enable them by symlinking to sites-enabled and reload.
#### How do I improve page load speed?
Enable gzip compression in your HTTP block. Set cache headers for static assets using expires directives. Use sendfile for efficient file serving. For caching, use proxy_cache_path to define cache storage and proxy_cache in location blocks to reduce backend load. Consider a CDN for global delivery.
#### What should I do if NGINX will not start?
Run nginx -t to check for configuration errors. The output shows the file and line number of any issues. Review error logs at /var/log/nginx/error.log for specific messages. Common causes include port conflicts, missing SSL certificates, syntax errors like missing semicolons, or permission issues. Access logs are in /var/log/nginx/access.log. Use tail -f to watch logs in real time.
#### How do I reload NGINX without downtime?
Run sudo nginx -s reload. NGINX starts new workers with updated configuration while old workers finish serving current requests.
#### How do I test my SSL configuration?
Visit ssllabs.com/ssltest and enter your domain. Aim for an A or A+ rating. The test identifies weak ciphers, protocol issues, and certificate problems.
#### Why do my static files return 404 errors?
Check your root or alias paths for typos. Verify files exist in the specified directory. Ensure NGINX has read permissions on files and execute permissions on directories. The www-data user must be able to read your files.
Ready to run NGINX websites on Virtarix VPS?
Compare website VPS sizes for NGINX server blocks, logs, SSL/TLS, root access, NVMe storage, snapshots, backups, and traffic headroom.
VPS S
For small websites and landing pages
- ✓ 3 cores
- ✓ 6 GB
- ✓ 50 GB NVMe
- ✓ Unlimited
VPS M
For growing sites and staging
- ✓ 6 cores
- ✓ 16 GB
- ✓ 100 GB NVMe
- ✓ Unlimited