Configuración de Server Blocks en Nginx: Guía Completa

Introducción

Los server blocks en Nginx son el equivalente a los virtual hosts de Apache, permitiendo que una sola instalación de Nginx aloje múltiples sitios web, aplicaciones y servicios en un servidor físico. Esta poderosa característica te permite servir diferente contenido basado en el nombre de dominio solicitado por el cliente, haciendo posible ejecutar sitios web ilimitados en una sola dirección IP de manera eficiente.

A diferencia del sistema de virtual hosts de Apache, Nginx utiliza server blocks con una sintaxis de configuración más optimizada. La arquitectura orientada a eventos de Nginx combinada con server blocks bien configurados puede manejar miles de conexiones concurrentes mientras sirve múltiples sitios web simultáneamente con una sobrecarga mínima de recursos.

Esta guía integral cubre todo lo que necesitas saber sobre configurar server blocks en Nginx. Aprenderás cómo configurar server blocks basados en nombre e IP, configurar SSL/TLS para HTTPS, implementar reglas de enrutamiento avanzadas, optimizar rendimiento, asegurar tus configuraciones y solucionar problemas comunes. Ya sea que estés alojando un blog simple o gestionando una infraestructura multi-sitio compleja, dominar los server blocks es esencial para una administración efectiva de Nginx.

Prerrequisitos

Antes de configurar server blocks, asegúrate de tener:

  • Nginx instalado y ejecutándose en Ubuntu/Debian o CentOS/Rocky Linux
  • Acceso root o sudo a tu servidor
  • Comprensión básica de la sintaxis de configuración de Nginx
  • Nombres de dominio con DNS configurado para apuntar a la IP de tu servidor
  • Familiaridad con permisos de archivos Linux y estructura de directorios
  • Conocimiento de editor de texto (nano, vim u otro)
  • Comprensión de conceptos básicos de redes y HTTP
  • Certificados SSL/TLS (si configuras server blocks HTTPS)

Entendiendo Server Blocks

Server Blocks Basados en Nombre

Los server blocks basados en nombre permiten múltiples dominios en una sola dirección IP:

Flujo de Petición del Cliente:
1. Cliente solicita http://example.com/page.html
2. Navegador envía petición HTTP con cabecera "Host: example.com"
3. Nginx recibe petición en dirección IP 192.168.1.100:80
4. Nginx compara cabecera Host contra directivas server_name configuradas
5. Nginx sirve contenido del server block coincidente
6. Si no encuentra coincidencia, sirve desde server block por defecto

Ejemplo básico:

# Ambos server blocks comparten la misma IP
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
}

server {
    listen 80;
    server_name another.com www.another.com;
    root /var/www/another.com;
}

Server Blocks Basados en IP

Los server blocks basados en IP asignan a cada sitio una dirección IP única:

# Cada server block tiene una IP diferente
server {
    listen 192.168.1.100:80;
    server_name example.com;
    root /var/www/example.com;
}

server {
    listen 192.168.1.101:80;
    server_name another.com;
    root /var/www/another.com;
}

Server Block Por Defecto

El server block por defecto maneja peticiones que no coinciden con ningún server_name configurado:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;  # Cerrar conexión sin respuesta
}

Estructura de Directorios de Server Blocks

Ubuntu y Debian

Nginx en Ubuntu/Debian usa estructura sites-available/sites-enabled:

/etc/nginx/
├── nginx.conf                # Configuración principal
├── sites-available/          # Todas las configs de server blocks
│   ├── default              # Server block por defecto
│   ├── example.com          # Server block personalizado
│   └── another.com          # Otro server block
├── sites-enabled/            # Server blocks habilitados (symlinks)
│   ├── default -> ../sites-available/default
│   └── example.com -> ../sites-available/example.com
└── snippets/                 # Snippets de configuración reutilizables

Flujo de trabajo:

  1. Crear configuración en sites-available/
  2. Habilitar con symlink a sites-enabled/
  3. Probar y recargar Nginx

CentOS, Rocky Linux y AlmaLinux

Los sistemas basados en Red Hat usan un enfoque más simple:

/etc/nginx/
├── nginx.conf                # Configuración principal
└── conf.d/                   # Todos los archivos *.conf se cargan
    ├── default.conf
    ├── example.com.conf
    └── another.com.conf

Flujo de trabajo:

  1. Crear archivo .conf en conf.d/
  2. Probar y recargar Nginx

Creando Tu Primer Server Block

Paso 1: Crear Directorio Document Root

Crear la estructura de directorios para tu sitio web:

# Ubuntu/Debian
sudo mkdir -p /var/www/example.com/html
sudo mkdir -p /var/www/example.com/logs

# CentOS/Rocky/AlmaLinux
sudo mkdir -p /usr/share/nginx/example.com/html
sudo mkdir -p /var/log/nginx/example.com

Paso 2: Establecer Permisos Apropiados

Configurar propiedad y permisos:

# Ubuntu/Debian
sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 755 /var/www/example.com

# CentOS/Rocky/AlmaLinux
sudo chown -R nginx:nginx /usr/share/nginx/example.com
sudo chmod -R 755 /usr/share/nginx/example.com

En sistemas con SELinux habilitado (CentOS/Rocky/AlmaLinux):

# Establecer contexto SELinux correcto
sudo chcon -R -t httpd_sys_content_t /usr/share/nginx/example.com/html

# Hacerlo permanente
sudo semanage fcontext -a -t httpd_sys_content_t "/usr/share/nginx/example.com/html(/.*)?"
sudo restorecon -Rv /usr/share/nginx/example.com

Paso 3: Crear Contenido de Prueba

Crear un archivo index.html simple:

# Ubuntu/Debian
sudo nano /var/www/example.com/html/index.html

# CentOS/Rocky/AlmaLinux
sudo nano /usr/share/nginx/example.com/html/index.html

Agregar el siguiente contenido:

<!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>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
        }
        .container {
            background: white;
            padding: 60px 40px;
            border-radius: 12px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
            max-width: 600px;
            text-align: center;
        }
        h1 {
            color: #009639;
            font-size: 2.5em;
            margin-bottom: 20px;
        }
        .info {
            background: #f0f8ff;
            padding: 20px;
            border-left: 4px solid #009639;
            margin: 30px 0;
            text-align: left;
        }
        .info strong {
            color: #009639;
        }
        p {
            color: #666;
            line-height: 1.6;
            margin: 15px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Welcome to Example.com</h1>
        <p>Your Nginx server block is configured correctly!</p>
        <div class="info">
            <strong>Server Information:</strong><br>
            Server Block: example.com<br>
            Document Root: /var/www/example.com/html<br>
            Status: Active and Running<br>
            Powered by: Nginx
        </div>
        <p>You can now upload your website content to this directory.</p>
    </div>
</body>
</html>

Paso 4: Crear Configuración de Server Block

Configuración Ubuntu/Debian

Crear el archivo de configuración del server block:

sudo nano /etc/nginx/sites-available/example.com

Agregar la configuración básica:

server {
    # Escuchar en IPv4 e IPv6
    listen 80;
    listen [::]:80;

    # Nombre del servidor
    server_name example.com www.example.com;

    # Document root
    root /var/www/example.com/html;
    index index.html index.htm index.nginx-debian.html;

    # Logging
    access_log /var/www/example.com/logs/access.log;
    error_log /var/www/example.com/logs/error.log;

    # Bloque location principal
    location / {
        try_files $uri $uri/ =404;
    }

    # Denegar acceso a archivos ocultos
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

Habilitar el server block:

# Crear symlink
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# Probar configuración
sudo nginx -t

# Recargar Nginx
sudo systemctl reload nginx

Configuración CentOS/Rocky/AlmaLinux

Crear el archivo de configuración:

sudo nano /etc/nginx/conf.d/example.com.conf

Agregar la configuración:

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;
    }
}

Probar y recargar:

# Probar configuración
sudo nginx -t

# Recargar Nginx
sudo systemctl reload nginx

Configuración Avanzada de Server Blocks

Múltiples Nombres de Dominio

Alojar múltiples dominios apuntando al mismo contenido:

server {
    listen 80;
    listen [::]:80;

    # Múltiples nombres de servidor
    server_name example.com www.example.com example.net www.example.net example.org;

    root /var/www/example.com/html;
    index index.html;

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

    # Registrar qué dominio fue accedido
    access_log /var/log/nginx/example-access.log combined;
}

Nombres de Servidor con Comodín

Coincidir todos los subdominios:

server {
    listen 80;
    server_name *.example.com .example.com;

    root /var/www/example.com/html;

    # Registrar subdominio en logs de acceso
    log_format subdomains '$host $remote_addr - $remote_user [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '"$http_referer" "$http_user_agent"';

    access_log /var/log/nginx/subdomains-access.log subdomains;

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

Server Blocks de Subdominios

Crear server blocks separados para diferentes subdominios:

# Dominio principal
server {
    listen 80;
    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

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

# Subdominio de blog
server {
    listen 80;
    server_name blog.example.com;

    root /var/www/example.com/blog;
    index index.html index.php;

    access_log /var/log/nginx/blog.example-access.log;
    error_log /var/log/nginx/blog.example-error.log;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

# Subdominio de API
server {
    listen 80;
    server_name api.example.com;

    root /var/www/example.com/api;

    access_log /var/log/nginx/api.example-access.log;
    error_log /var/log/nginx/api.example-error.log;

    # Cabeceras CORS para API
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;

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

Enrutamiento Dinámico de Subdominios

Enrutar subdominios dinámicamente basados en estructura de directorios:

server {
    listen 80;
    server_name ~^(?<subdomain>.+)\.example\.com$;

    root /var/www/subdomains/$subdomain;
    index index.html;

    # Si el directorio no existe, mostrar error
    if (!-d /var/www/subdomains/$subdomain) {
        return 404;
    }

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

Redirección de URL

Redirigir www a no-www (o viceversa):

# Redirigir www a no-www
server {
    listen 80;
    listen [::]:80;
    server_name www.example.com;
    return 301 http://example.com$request_uri;
}

server {
    listen 80;
    listen [::]:80;
    server_name example.com;

    root /var/www/example.com/html;
    index index.html;

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

Alternativa usando declaración if (menos preferido):

server {
    listen 80;
    server_name example.com www.example.com;

    # Redirigir www a no-www
    if ($host = www.example.com) {
        return 301 http://example.com$request_uri;
    }

    root /var/www/example.com/html;
    index index.html;

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

Páginas de Error Personalizadas

Configurar páginas de error personalizadas por server block:

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;
    index index.html;

    # Páginas de error personalizadas
    error_page 400 /errors/400.html;
    error_page 401 /errors/401.html;
    error_page 403 /errors/403.html;
    error_page 404 /errors/404.html;
    error_page 500 502 503 504 /errors/50x.html;

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

    # Ubicación para páginas de error
    location ^~ /errors/ {
        internal;
        root /var/www/example.com/html;
    }
}

Crear páginas de error personalizadas:

sudo mkdir -p /var/www/example.com/html/errors
sudo nano /var/www/example.com/html/errors/404.html

Ejemplo de página 404:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>404 - Page Not Found</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0;
            padding: 20px;
        }
        .error-container {
            text-align: center;
            color: white;
        }
        h1 {
            font-size: 120px;
            margin: 0;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
        h2 {
            font-size: 36px;
            margin: 20px 0;
        }
        p {
            font-size: 18px;
            margin: 20px 0;
        }
        a {
            display: inline-block;
            padding: 15px 30px;
            background: white;
            color: #667eea;
            text-decoration: none;
            border-radius: 30px;
            font-weight: bold;
            margin-top: 20px;
            transition: transform 0.3s;
        }
        a:hover {
            transform: scale(1.05);
        }
    </style>
</head>
<body>
    <div class="error-container">
        <h1>404</h1>
        <h2>Page Not Found</h2>
        <p>The page you're looking for doesn't exist or has been moved.</p>
        <a href="/">Return to Homepage</a>
    </div>
</body>
</html>

Server Blocks SSL/TLS

Configurar server blocks HTTPS:

Server Block HTTPS Básico

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

    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    # Certificados SSL
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # Protocolos y cifrados SSL
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # Caché de sesión SSL
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # Cabecera HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

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

    access_log /var/log/nginx/example-ssl-access.log;
    error_log /var/log/nginx/example-ssl-error.log;
}

# Redirección HTTP a HTTPS
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

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

Configuración Let's Encrypt

Después de obtener certificados Let's Encrypt:

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

    server_name example.com www.example.com;

    root /var/www/example.com/html;
    index index.html;

    # Certificados Let's Encrypt
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Configuración SSL fuerte
    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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Configuración de sesión SSL
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # Cabeceras de seguridad
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

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

    # Desafío ACME de Let's Encrypt
    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/example.com/html;
    }
}

# Redirección HTTP a HTTPS
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    # Permitir desafíos Let's Encrypt
    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/example.com/html;
    }

    # Redirigir todo lo demás a HTTPS
    location / {
        return 301 https://$server_name$request_uri;
    }
}

Snippet de Configuración SSL

Crear configuración SSL reutilizable:

sudo nano /etc/nginx/snippets/ssl-params.conf

Agregar:

# Protocolos SSL
ssl_protocols TLSv1.2 TLSv1.3;

# Cifrados SSL
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;

# Sesión SSL
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# Cabeceras de seguridad
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

Usar en server block:

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    # Incluir parámetros SSL
    include snippets/ssl-params.conf;

    root /var/www/example.com/html;

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

Optimización de Rendimiento

Habilitar Compresión

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    # Compresión Gzip
    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/atom+xml
        image/svg+xml;
    gzip_disable "msie6";

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

Caché de Navegador

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    # Cachear archivos estáticos
    location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
        expires 365d;
        add_header Cache-Control "public, no-transform, immutable";
        access_log off;
    }

    location ~* \.(css|js)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }

    location ~* \.(woff|woff2|ttf|eot)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

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

Caché FastCGI para PHP

# Definir zona de caché FastCGI
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2
                   keys_zone=PHPCACHE:100m
                   inactive=60m
                   max_size=1g;

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;
    index index.php index.html;

    # Omitir caché para ciertas condiciones
    set $skip_cache 0;

    # Peticiones POST y URLs con query strings
    if ($request_method = POST) {
        set $skip_cache 1;
    }
    if ($query_string != "") {
        set $skip_cache 1;
    }

    # No cachear páginas de admin
    if ($request_uri ~* "/wp-admin/|/admin/") {
        set $skip_cache 1;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # Caché FastCGI
        fastcgi_cache PHPCACHE;
        fastcgi_cache_valid 200 60m;
        fastcgi_cache_valid 404 10m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;

        # Agregar cabecera de estado de caché
        add_header X-Cache-Status $upstream_cache_status;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }
}

Configuración de Seguridad

Limitación de Tasa

# Definir zona de límite de tasa
limit_req_zone $binary_remote_addr zone=limitbyaddr:10m rate=10r/s;
limit_req_status 429;

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    location / {
        # Aplicar límite de tasa
        limit_req zone=limitbyaddr burst=20 nodelay;
        try_files $uri $uri/ =404;
    }

    # Límite diferente para API
    location /api/ {
        limit_req zone=limitbyaddr burst=10 nodelay;
        try_files $uri $uri/ =404;
    }
}

Limitación de Conexiones

# Definir zona de límite de conexiones
limit_conn_zone $binary_remote_addr zone=connlimit:10m;

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    # Limitar conexiones concurrentes
    limit_conn connlimit 10;

    location /download/ {
        # Más restrictivo para descargas
        limit_conn connlimit 2;
        limit_rate 500k;  # Límite de ancho de banda por conexión
    }

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

Control de Acceso Basado en IP

server {
    listen 80;
    server_name admin.example.com;

    root /var/www/example.com/admin;

    # Permitir solo IPs específicas
    allow 192.168.1.0/24;
    allow 10.0.0.100;
    allow 203.0.113.50;
    deny all;

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

Autenticación Básica

server {
    listen 80;
    server_name secure.example.com;

    root /var/www/example.com/secure;

    # Autenticación básica
    auth_basic "Restricted Area";
    auth_basic_user_file /etc/nginx/.htpasswd;

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

    # Deshabilitar auth para ubicación específica
    location /public/ {
        auth_basic off;
    }
}

Crear archivo de contraseñas:

# Instalar utilidad htpasswd
sudo apt install apache2-utils  # Ubuntu/Debian
sudo yum install httpd-tools    # CentOS/Rocky

# Crear archivo de contraseñas
sudo htpasswd -c /etc/nginx/.htpasswd username

Cabeceras de Seguridad

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    # Cabeceras de seguridad
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;

    # Eliminar firma del servidor
    server_tokens off;

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

Server Block de Reverse Proxy

Configurar Nginx como reverse proxy:

upstream backend {
    least_conn;
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
}

server {
    listen 80;
    server_name app.example.com;

    access_log /var/log/nginx/app-access.log;
    error_log /var/log/nginx/app-error.log;

    location / {
        proxy_pass http://backend;

        # Cabeceras de proxy
        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;

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffering
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        proxy_busy_buffers_size 8k;
    }

    # Archivos estáticos servidos por Nginx
    location /static/ {
        alias /var/www/example.com/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

Configuración de Logging

Formatos de Log Personalizados

# Definir formatos de log personalizados
log_format main '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent"';

log_format detailed '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time';

log_format json escape=json '{'
                '"time": "$time_local",'
                '"remote_addr": "$remote_addr",'
                '"request": "$request",'
                '"status": $status,'
                '"body_bytes_sent": $body_bytes_sent,'
                '"http_referer": "$http_referer",'
                '"http_user_agent": "$http_user_agent"'
                '}';

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    # Usar formato de log personalizado
    access_log /var/log/nginx/example-access.log detailed;
    error_log /var/log/nginx/example-error.log warn;

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

Logging Condicional

server {
    listen 80;
    server_name example.com;

    root /var/www/example.com/html;

    # Map para determinar si se debe registrar
    map $request_uri $loggable {
        ~*\.(gif|jpg|jpeg|png|css|js|ico|svg)$ 0;
        default 1;
    }

    # No registrar health checks
    map $http_user_agent $log_ua {
        ~*pingdom|uptime 0;
        default 1;
    }

    # Combinar condiciones
    set $log 0;
    if ($loggable) {
        set $log 1;
    }
    if ($log_ua = 0) {
        set $log 0;
    }

    access_log /var/log/nginx/example-access.log combined if=$log;

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

Solución de Problemas

Probar Configuración

Siempre probar configuración antes de recargar:

sudo nginx -t

Salida esperada:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Verificar Configuración de Server Block

# Ver todos los server blocks y sus configuraciones
sudo nginx -T

# Verificar coincidencias específicas de server_name
sudo nginx -T | grep -A 10 "server_name example.com"

Problemas Comunes

Problema: "conflicting server name"

# Verificar directivas server_name duplicadas
sudo nginx -T | grep "server_name" | sort | uniq -d

# Solución: Asegurar server_name único o usar default_server

Problema: Server block incorrecto servido

# Verificar orden de server blocks y default_server
sudo nginx -T | grep -A 5 "listen.*default_server"

# Probar con curl
curl -H "Host: example.com" http://your_ip/

Problema: Errores de permiso denegado

# Verificar permisos de archivos
ls -la /var/www/example.com/

# Corregir permisos
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

# Verificar SELinux (CentOS/Rocky/AlmaLinux)
sudo getenforce
sudo chcon -R -t httpd_sys_content_t /usr/share/nginx/example.com/html

Problema: Errores de certificado SSL

# Verificar que existen archivos de certificado
sudo ls -la /etc/letsencrypt/live/example.com/

# Probar configuración SSL
sudo nginx -t

# Verificar validez del certificado
sudo openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -text -noout

Mejores Prácticas

Configuración Organizada

Mantener archivos de server blocks organizados:

# Convención de nombres Ubuntu/Debian
/etc/nginx/sites-available/
├── default                # Catch-all por defecto
├── example.com            # Dominio principal
├── blog.example.com       # Subdominio
└── api.example.com        # Subdominio API

Usar Include para Configuraciones Comunes

# /etc/nginx/snippets/common-locations.conf
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

location = /favicon.ico {
    access_log off;
    log_not_found off;
}

location = /robots.txt {
    access_log off;
    log_not_found off;
}

# Usar en server block
server {
    listen 80;
    server_name example.com;
    root /var/www/example.com/html;

    include snippets/common-locations.conf;

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

Documentación

Documentar cada server block:

#
# Server Block: example.com
# Created: 2024-01-15
# Modified: 2024-01-20
# Purpose: Main company website
# Contact: [email protected]
#

server {
    listen 80;
    server_name example.com www.example.com;
    # ...
}

Respaldos Regulares

Respaldar configuraciones de server blocks:

# Ubuntu/Debian
sudo tar -czf nginx-sites-backup-$(date +%F).tar.gz /etc/nginx/sites-available/

# CentOS/Rocky/AlmaLinux
sudo tar -czf nginx-conf-backup-$(date +%F).tar.gz /etc/nginx/conf.d/

Monitoreo

Monitorear acceso y errores de server blocks:

# Log de acceso en tiempo real
sudo tail -f /var/log/nginx/example-access.log

# Log de errores en tiempo real
sudo tail -f /var/log/nginx/example-error.log

# Contar peticiones por código de estado
sudo awk '{print $9}' /var/log/nginx/example-access.log | sort | uniq -c | sort -nr

Conclusión

Los server blocks son la base del alojamiento multi-sitio de Nginx, permitiendo gestión eficiente de múltiples sitios web en un solo servidor. Esta guía ha cubierto todo desde server blocks básicos basados en nombre hasta configuraciones SSL/TLS avanzadas, hardening de seguridad, optimización de rendimiento y solución de problemas.

Conclusiones clave:

  • Los server blocks basados en nombre permiten sitios web ilimitados en una dirección IP con sobrecarga mínima de recursos
  • La estructura de directorios y permisos apropiados son críticos para seguridad y funcionalidad
  • Los server blocks SSL/TLS con HTTP/2 proporcionan alojamiento web moderno y seguro
  • La optimización de rendimiento a través de caché y compresión mejora la experiencia del usuario
  • Las características de seguridad como limitación de tasa, control de acceso y cabeceras de seguridad protegen tus sitios
  • Las pruebas regulares, monitoreo y respaldos aseguran operación confiable

Domina la configuración de server blocks para alojar eficientemente múltiples sitios web, implementar patrones de enrutamiento avanzados y construir infraestructuras web escalables. Continúa explorando temas avanzados como server blocks dinámicos con Lua, integración con balanceo de carga, estrategias avanzadas de caché y gestión automatizada de certificados SSL con Certbot.