f in x
> cd .. / HUB_EDITORIALE
Sistemi Operativi & Sicurezza

Nginx and Web Server Configuration: The Definitive Pillar Guide for Production

[2026-06-17] Author: Ing. Calogero Bono

If your web server crashes at the first traffic spike, or if pages load at a glacial pace, it's not the code. It's the web server configuration. We at Meteora Web have seen dozens of projects arrive with sluggish Apache, unoptimized PHP-FPM, and Nginx running with defaults. Result: awful response times, wasted resources, and lost customers. In this pillar page we give you everything you need to turn Nginx into a production-ready missile. No theory, only commands and decisions you can apply today.

Why Nginx is the starting point for every modern application

Nginx isn't just a web server. It's a reverse proxy, load balancer, static content accelerator, and API gateway, all in one. Its event-driven architecture makes it lighter and more scalable than Apache on the same hardware. We use it as the front-end for every project: whether it's Laravel, WordPress, or a Vue SPA, Nginx sits in front and handles traffic.

The concrete benefit: with Nginx you can serve thousands of simultaneous connections with very little RAM. Worker processes are configurable based on CPU cores, keepalive queues reduce three-way handshakes, and static file caching offloads PHP or Python. In short: you spend less on servers and get better performance.

Sponsored Protocol

Installation and basic configuration

On Ubuntu (our go‑to distribution) just two commands:

sudo apt update
sudo apt install nginx

Then enable and start:

sudo systemctl enable nginx
sudo systemctl start nginx
curl localhost

If you see the Nginx welcome page, you're set. Then create a virtual host for your domain.

Virtual host structure

In Nginx, each domain has a file in /etc/nginx/sites-available/, enabled via a symlink in /etc/nginx/sites-enabled/. A minimal static site:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/yourdomain;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}

After each change, always run sudo nginx -t and then sudo systemctl reload nginx.

Nginx as a reverse proxy

This is where Nginx shines. All modern apps (PHP-FPM, Node.js, Python) run on internal ports. Nginx exposes port 80/443 and forwards requests. This:

  • Protects the application from direct attacks
  • Centralizes SSL, security headers, and limits
  • Places static cache in front of everything

Reverse proxy for PHP-FPM (WordPress, Laravel)

server {
    listen 80;
    server_name my-site.com;
    root /var/www/my-site/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_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    location ~ /\.ht {
        deny all;
    }
}

Note the Unix socket – faster than TCP. On shared servers or containers, use TCP (127.0.0.1:9000).

Sponsored Protocol

Reverse proxy for Node.js (Express, Nuxt, Next)

server {
    listen 80;
    server_name api.my-site.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

The proxy_http_version 1.1 and upgrade headers are essential for WebSockets.

SSL/TLS with Let's Encrypt and auto‑renewal

We dropped paid certificates the moment Let's Encrypt made TLS free and automated. Setup:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot modifies your server blocks and sets up HTTP→HTTPS redirect. Then enable automatic renewal:

Sponsored Protocol

sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

Always check: sudo systemctl list-timers | grep certbot.

Strong TLS configuration

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

Test your domain at SSL Labs.

Performance tuning: workers, cache, keepalive

Worker processes and connections

worker_processes auto;
events {
    worker_connections 1024;
}

auto sets workers equal to CPU cores. Raise worker_connections to 1024 from default 512.

Keepalive and buffers

http {
    keepalive_timeout 65;
    keepalive_requests 100;
    client_body_buffer_size 128k;
    client_max_body_size 10m;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
}

Static cache

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

Load balancing with Nginx

upstream backend {
    server 10.0.0.1:3000 weight=3;
    server 10.0.0.2:3000;
    server 10.0.0.3:3000 backup;
}
server {
    listen 80;
    server_name loadbalanced.com;
    location / {
        proxy_pass http://backend;
    }
}
  • Round Robin (default)
  • Least Connections: least_conn;
  • IP Hash: ip_hash;

For more advanced scenarios, check our article on API Gateway deployment patterns.

Sponsored Protocol

Security headers and basic protection

add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy strict-origin-when-cross-origin;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";

Rate limiting for API protection

http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    server {
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://backend;
        }
    }
}

Nginx with Docker

version: '3'
services:
  nginx:
    image: nginx:stable-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - php
      - node
  php:
    image: php:8.3-fpm-alpine
    volumes:
      - ./html:/usr/share/nginx/html

Access log and error log: parsing and monitoring

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" $request_time';
    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log warn;
}

Send logs to a tool like Grafana for alerts. See our Grafana guide.

Sponsored Protocol

Nginx vs Apache vs Caddy

  • Apache: useful for embedded PHP (mod_php), but lags in performance and concurrency.
  • Caddy: easy setup with automatic HTTPS, but smaller ecosystem. Good for tiny projects.
  • Nginx: the best balance of flexibility, performance, and community. Our default choice.

In summary – what to do now

  1. Install Nginx on a test server and create a virtual host with free SSL.
  2. Set up reverse proxy for your app (PHP, Node, Python).
  3. Apply performance tuning (workers, keepalive, static cache).
  4. Configure rate limiting and security headers on at least one public endpoint.
  5. Set up access log monitoring.

We use these same techniques every day for clients across Italy. If you'd like a second pair of eyes on your server, get in touch.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()