Nginx, para servidores con multiples vhosts a pelo (sin panel) + PHP-FPM
Nginx
Instalado de forma oficial siguiendo la ruta de instalación estándar de Ubuntu.
Mejoras sobre la configuración original
A continuación se describen las mejoras implementadas sobre la configuración estándar. Estas optimizaciones se centran en:
- Reducción del TTFB (Time To First Byte)
- Refuerzo de seguridad (especialmente en ciphers)
- Optimización de rendimiento general
Las directivas que comienzan con # comentario
son personalizaciones, ya sea por adición o modificación.
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535; # Incrementa el límite de archivos abiertos
# load_module modules/ngx_http_modsecurity_module.so;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
# Configuración optimizada de eventos
events {
worker_connections 1024;
use epoll; # Método eficiente para Linux
multi_accept on; # Acepta múltiples conexiones por proceso
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
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;
## Mejoras de rendimiento y seguridad probadas durante años
sendfile on; # Optimiza el envío de archivos
tcp_nopush on; # Optimiza paquetes TCP
tcp_nodelay on; # Reduce latencia
client_header_timeout 60s; # Tiempo máximo para recibir cabeceras
client_body_timeout 60s; # Tiempo máximo para recibir cuerpo
client_header_buffer_size 2k; # Tamaño del buffer para cabeceras
client_body_buffer_size 256k; # Tamaño del buffer para el cuerpo
client_max_body_size 256m; # Tamaño máximo de petición
large_client_header_buffers 4 8k; # Buffers para cabeceras grandes
send_timeout 60s; # Tiempo máximo de envío
keepalive_timeout 30s; # Tiempo máximo de conexión persistente
reset_timedout_connection on; # Libera conexiones que expiran
server_tokens off; # Oculta la versión de Nginx
server_name_in_redirect off; # No incluye nombre del servidor en redirecciones
server_names_hash_max_size 512; # Tamaño máximo de la tabla hash
server_names_hash_bucket_size 512; # Tamaño del bucket hash
# Compresión para optimizar el tráfico
gzip on; # Activa la compresión
gzip_static on; # Busca versiones pre-comprimidas
gzip_vary on; # Añade cabecera Vary
gzip_comp_level 6; # Nivel de compresión (equilibrio rendimiento/tamaño)
gzip_min_length 1024; # Tamaño mínimo para comprimir
gzip_buffers 16 8k; # Buffers para compresión
gzip_types text/plain text/css text/javascript text/js text/xml application/json application/javascript application/x-javascript application/xml application/xml+rss application/x-font-ttf image/svg+xml font/opentype;
gzip_proxied any; # Comprime respuestas proxy
gzip_disable "MSIE [1-6]\."; # Desactiva para IEs antiguos
# Configuración de proxy
proxy_redirect off;
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_pass_header Set-Cookie;
proxy_buffers 32 4k;
proxy_connect_timeout 30s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
# Configuración para Cloudflare (actualizada: 28/09/2023)
# Permite identificar la IP real del visitante detrás de Cloudflare
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
#set_real_ip_from 2400:cb00::/32; # IPv6 (comentados por compatibilidad)
#set_real_ip_from 2606:4700::/32;
#set_real_ip_from 2803:f800::/32;
#set_real_ip_from 2405:b500::/32;
#set_real_ip_from 2405:8100::/32;
#set_real_ip_from 2c0f:f248::/32;
#set_real_ip_from 2a06:98c0::/29;
real_ip_header CF-Connecting-IP;
# Configuración SSL que cumple con PCI Compliance
# Basado en https://blog.ss88.us/secure-ssl-https-nginx-vestacp
ssl_protocols TLSv1.2 TLSv1.3; # Elimina protocolos obsoletos (SSLv3)
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !MEDIUM";
#ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
ssl_dhparam dh4096.pem;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
# Configuración de caché
# IMPORTANTE: Verificar permisos y propiedad de los directorios
# Especialmente útil en entornos con alta carga de solicitudes
proxy_cache_path /var/cache/nginx levels=2 keys_zone=cache:10m inactive=60m max_size=1024m;
proxy_cache_key "$host$request_uri $cookie_user";
proxy_temp_path /var/cache/nginx/temp;
proxy_ignore_headers Expires Cache-Control;
proxy_cache_use_stale error timeout invalid_header http_502;
proxy_cache_valid any 1d;
# Bypass de caché para sesiones activas
map $http_cookie $no_cache {
default 0;
~SESS 1;
~wordpress_logged_in 1;
}
# Configuración de caché de archivos
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors off;
# Configuración de cabeceras y expiración
# Mapa de tiempos de expiración según tipo de contenido
map $sent_http_content_type $expires {
default off;
text/html epoch; # No cachear HTML (dinámico)
text/css max; # Cachear CSS al máximo
application/javascript max; # Cachear JS al máximo
~image/ max; # Cachear imágenes al máximo
}
##
# Configuración de Hosts Virtuales
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Permisos y propiedad del caché de Nginx
Para que Nginx funcione correctamente con la configuración de caché definida, los directorios implicados deben tener los permisos y propiedad adecuados:
Propiedad (Owner y Grupo)
-
Propietario: El usuario con el que se ejecuta Nginx (típicamente
www-data
en Ubuntu) -
Grupo: El grupo correspondiente (típicamente
www-data
en Ubuntu)
Permisos recomendados
-
/var/cache/nginx
: 750 (drwxr-x---) -
/var/cache/nginx/temp
: 750 (drwxr-x---)
Comandos para establecer estos permisos
# Crear los directorios si no existen
mkdir -p /var/cache/nginx/temp
# Establecer la propiedad correcta
chown -R www-data:www-data /var/cache/nginx
# Establecer los permisos adecuados
chmod 750 /var/cache/nginx
chmod 750 /var/cache/nginx/temp
Estos permisos garantizan que los directorios sean accesibles únicamente por el usuario que ejecuta Nginx, mejorando la seguridad del sistema al evitar accesos no autorizados a posible información sensible almacenada en caché.
Configuración de los Virtual Hosts
Existen dos enfoques para gestionar los archivos de configuración de hosts virtuales:
1. Enfoque centralizado (recomendado para entornos de producción)
Los archivos de configuración se colocan en /etc/nginx/sites-available/
y se enlazan simbólicamente a /etc/nginx/sites-enabled/
:
ln -s /etc/nginx/sites-available/domain.tld.conf /etc/nginx/sites-enabled/
Ventajas:
- Mayor control y seguridad
- Centralización de configuraciones
- Facilita auditorías de seguridad
- Previene cambios accidentales por usuarios sin privilegios
2. Enfoque por usuario (útil en entornos único administrador)
Los archivos se colocan en directorios de usuario y se enlazan a la configuración principal.
Limitaciones:
- Los usuarios sin acceso
sudo
no podrán:- Validar configuraciones con
nginx -t
- Recargar el servicio con
systemctl reload nginx
- Reiniciar el servicio con
systemctl restart nginx
- Validar configuraciones con
Configuración inicial (pre-certificado)
Antes de obtener el certificado SSL, se debe crear un archivo de configuración sin asignación específica de puertos. Certbot (Let's Encrypt) se encargará de esto durante el proceso de certificación.
Ejemplo:
server {
server_name domain.tld www.domain.tld;
root /home/user/web/domain.tld/domain/dist; # Ruta para despliegues JS/VUE/React
index index.html;
# Es ALTAMENTE RECOMENDABLE activar estas cabeceras de seguridad
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
charset utf-8;
## Logs ubicados en la carpeta del usuario para facilitar su acceso
access_log /home/user/logs/web/domain.tld.log combined;
error_log /home/user/logs/web/domain.tld.error.log error;
expires $expires; # Utiliza la variable map definida en nginx.conf
# Si has compilado y tienes operativo mod_security, descomenta la línea siguiente
# include /etc/nginx/modsec/active.conf;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
#error_page 404 /index.php;
location / {
root /home/user/web/domain.tld/domain/dist/;
index index.html;
try_files $uri $uri/ /index.html; # Configuración para SPAs (React, Vue, etc.)
#try_files $uri $uri/ =404; # Alternativa para sitios estáticos
location ~* ^.+\.(jpeg|jpg|png|gif|bmp|ico|svg|css|js)$ {
access_log off;
log_not_found off;
expires max;
}
}
# Bloqueo de archivos de configuración sensibles
location ~* "/\.(htaccess|htpasswd|env)$" {
deny all;
return 404;
}
# Bloqueo de archivos y directorios ocultos
location ~ /\.(?!well-known).* {
deny all;
}
}
Configuración de la variable $expires
Esta variable se declara mediante la directiva map
en el archivo nginx.conf
. Si no está declarada, su uso generará un error.
map $sent_http_content_type $expires {
default off;
text/html epoch; # No cachear HTML (fecha 1/1/1970)
text/css max; # Cachear CSS al máximo
application/javascript max; # Cachear JS al máximo
~image/ max; # Cachear imágenes al máximo
}
Buena práctica: Después de cada modificación, ejecutar
nginx -t
para verificar la sintaxis de la configuración antes de recargar el servicio.
Certificados con Certbot
Certbot ya viene instalado en la mayoría de distribuciones modernas. Para nuevas instalaciones, su manual oficial proporciona instrucciones detalladas.
Se recomienda crear certificados específicos por dominio en lugar de certificados wildcard:
certbot --nginx -d domain.tld -d www.domain.tld
Nota: En sistemas con restricciones de red, es necesario abrir temporalmente los puertos 80 y 443 durante el proceso de renovación.
PHP
Instalado desde el repositorio de Ondrej Surý, específicamente la versión PHP-FPM.
Ubicaciones importantes:
- Configuración de pools:
/etc/php/X.X/fpm/pool.d/www.conf
(donde X.X es la versión de PHP) - Configuración general:
/etc/php/X.X/fpm/php.ini
Configuración crítica para logging
Es esencial modificar la configuración del pool para activar el registro de errores:
- Editar
/etc/php/X.X/fpm/pool.d/www.conf
y cambiar:catch_workers_output = yes
Sin esta modificación, PHP-FPM no registrará correctamente los errores en los logs de los dominios virtuales, lo que puede dificultar enormemente la depuración. Para más información, consulte PHP log cuando usamos PHP-FPM con host virtuales.
JIT Compiler
PHP 8.x introduce el compilador JIT (Just-In-Time), que puede mejorar significativamente el rendimiento en determinados escenarios. Sin embargo, puede presentar desafíos durante el desarrollo activo con cambios frecuentes.
Para más información sobre la activación y configuración del JIT, consulte Activar PHP8.2 JIT Compiler.
Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris
Si necesitas soporte profesional puedes contratar con Castris soporte profesional.