Estructura de los archivos de configuración de Nginx
Una guía completa sobre cómo se organizan los archivos de configuración de Nginx en disco, cómo se anidan los contextos entre sí, cómo las directivas include cargan archivos externos y cómo funciona realmente la herencia de directivas.
Instalaste Nginx. Abriste /etc/nginx/nginx.conf y encontraste un archivo que incluye otros archivos, que a su vez incluyen más archivos. Algunas directivas están dentro de llaves, otras flotan en el nivel superior. Antes de configurar server blocks, SSL o reverse proxies, necesitas el mapa.
Este artículo te da ese mapa. Sin recetas. Solo el modelo mental de cómo funciona la configuración de Nginx, para que puedas leer y modificar cualquier configuración que encuentres.
¿Dónde se almacenan los archivos de configuración de Nginx?
En Debian 12 y Ubuntu 24.04, el paquete apt instala todo bajo /etc/nginx/. Así se ve ese directorio en una instalación nueva:
/etc/nginx/
├── nginx.conf # Main config entry point
├── mime.types # Maps file extensions to MIME types
├── conf.d/ # Drop-in config files (*.conf auto-included)
├── sites-available/ # All virtual host config files
│ └── default # Default server block
├── sites-enabled/ # Symlinks to active virtual hosts
│ └── default -> ../sites-available/default
├── snippets/ # Reusable config fragments
│ └── fastcgi-php.conf
├── modules-available/ # Available dynamic module configs
├── modules-enabled/ # Symlinks to loaded modules
├── fastcgi.conf # FastCGI directive defaults
├── fastcgi_params # FastCGI parameter mappings
├── proxy_params # Proxy header defaults
├── scgi_params # SCGI parameter mappings
├── uwsgi_params # uWSGI parameter mappings
├── koi-utf # Character set mapping files
├── koi-win
└── win-utf
Puedes verificarlo en tu propio servidor:
ls -la /etc/nginx/
El archivo que editas con más frecuencia no es nginx.conf en sí. Normalmente es un archivo dentro de sites-available/ o conf.d/. El nginx.conf principal establece los valores globales por defecto y carga esos archivos a través de directivas include.
¿Cómo se organizan los archivos de configuración de Nginx?
La configuración de Nginx usa un árbol de contextos (contexts) anidados. Cada contexto es una directiva de bloque envuelta en llaves. Las directivas colocadas dentro de un contexto solo aplican dentro de ese alcance.
La jerarquía se ve así:
main (top level, outside any braces)
├── events { }
└── http { }
└── server { }
└── location { }
Cada directiva en la configuración vive en uno de estos niveles. El contexto main es el archivo en sí. Todo lo demás se anida dentro.
¿Qué controla el contexto main?
El contexto main es todo lo que está fuera de cualquier directiva de bloque en nginx.conf. Controla ajustes a nivel de proceso que afectan toda la instancia de Nginx.
Directivas clave en este nivel:
| Directiva | Propósito | Valor típico |
|---|---|---|
user |
Usuario del SO bajo el que corren los worker processes | www-data |
worker_processes |
Número de worker processes | auto (coincide con núcleos de CPU) |
pid |
Ruta al archivo PID | /run/nginx.pid |
error_log |
Ruta y nivel del log de errores global | /var/log/nginx/error.log |
include |
Carga configuraciones de módulos | /etc/nginx/modules-enabled/*.conf |
Este es el contexto main del nginx.conf por defecto en Debian 12:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
Estas cinco líneas se ejecutan antes que todo lo demás. Establecen bajo qué usuario corre Nginx, cuántos workers inicia y dónde escribe los errores.
¿Qué va en el bloque events?
El bloque events configura cómo Nginx maneja las conexiones a nivel del sistema operativo. Se ubica dentro del contexto main.
events {
worker_connections 768;
# multi_accept on;
}
worker_connections establece el número máximo de conexiones simultáneas por worker process. Con worker_processes auto en una máquina de 4 núcleos, obtienes 4 x 768 = 3.072 conexiones concurrentes. El valor por defecto del código fuente de Nginx es 512, pero Debian lo establece en 768.
Optimización del rendimiento de Nginx en un VPS
¿Para qué sirve el contexto http?
El bloque http contiene toda la configuración relacionada con el manejo de tráfico HTTP. Cada bloque server vive dentro de él. Las directivas que coloques aquí aplican a todos los virtual hosts, a menos que un bloque server o location las sobreescriba.
http {
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Observa server_tokens off; cerca del inicio. Esto oculta el número de versión de Nginx en los headers de respuesta. Revelar la versión ayuda a los atacantes a apuntar a vulnerabilidades conocidas. El valor por defecto de Debian no incluye esta línea. Agrégala.
Las dos líneas include al final son la forma en que Nginx carga las configuraciones reales de tus sitios. Cubriremos include en detalle más adelante.
¿Cómo funcionan los bloques server?
Un bloque server define un virtual host. Vive dentro del contexto http. Cada bloque server escucha en una combinación de dirección/puerto y asocia peticiones según el header Host.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Cuando llega una petición, Nginx elige el bloque server comparando server_name con el header Host. Si ningún bloque coincide, Nginx usa el default_server:
listen 80 default_server;
Normalmente creas un archivo por dominio en sites-available/, cada uno con un bloque server (o dos, si tienes tanto HTTP como HTTPS).
Server Blocks de Nginx: Aloja Varios Dominios en un VPS
¿Cómo resuelve Nginx los bloques location?
Un bloque location define cómo Nginx maneja las peticiones para patrones de URI específicos. Vive dentro de un bloque server (o dentro de otro location).
Nginx evalúa los bloques location en un orden específico:
| Modificador | Tipo | Ejemplo | Comportamiento |
|---|---|---|---|
= |
Coincidencia exacta | location = / |
Coincide solo con /. Detiene la búsqueda inmediatamente. |
^~ |
Prefijo prioritario | location ^~ /images/ |
Coincide por prefijo. Omite la verificación de regex. |
~ |
Regex (distingue mayúsculas) | location ~ \.php$ |
La primera coincidencia regex gana. |
~* |
Regex (no distingue mayúsculas) | location ~* \.(jpg|png)$ |
La primera coincidencia regex gana. |
| (ninguno) | Prefijo | location /api/ |
El prefijo más largo gana, pero un regex puede sobreescribirlo. |
El algoritmo de coincidencia:
- Nginx verifica todas las locations de prefijo y recuerda la coincidencia más larga.
- Si esa coincidencia usa
=o^~, se detiene. La usa. - De lo contrario, verifica las locations regex en el orden en que aparecen en el archivo de configuración.
- La primera coincidencia regex gana. Si ninguna regex coincide, usa el prefijo más largo del paso 1.
Esto significa que el orden de las locations de prefijo en tu archivo de configuración no importa. Pero el orden de las regex sí.
¿Cómo funciona la directiva include?
La directiva include inserta el contenido de otro archivo (o archivos que coincidan con un patrón glob) en la posición actual de la configuración. Nginx la resuelve al momento de cargar la configuración, antes de que se analice en su totalidad.
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
El glob *.conf coincide con todos los archivos que terminan en .conf en ese directorio. El glob * coincide con todo. Ambos son patrones comunes.
La directiva include funciona en cualquier contexto. Puedes usarla dentro de bloques http, server o location:
server {
listen 443 ssl;
include snippets/ssl-params.conf;
}
Así es como funciona snippets/. Escribes un fragmento reutilizable una vez y lo incluyes donde lo necesites.
Un detalle a tener en cuenta: Nginx no arranca si un include apunta a un archivo que no existe y la ruta no es un glob. Si escribes include /etc/nginx/conf.d/*.conf; y el directorio está vacío, Nginx arranca sin problemas (los globs pueden no coincidir con nada). Pero include /etc/nginx/ssl.conf; fallará si ese archivo no existe.
¿Cuál es la diferencia entre sites-available, sites-enabled y conf.d?
Estos tres directorios tienen propósitos diferentes. Debian y Ubuntu usan los tres.
| Directorio | Propósito | Cómo lo lee Nginx | Cuándo usarlo |
|---|---|---|---|
sites-available/ |
Almacena todas las configuraciones de virtual hosts | No se lee directamente | Siempre. Un archivo por dominio. |
sites-enabled/ |
Contiene symlinks a las configuraciones activas | Se incluye vía include /etc/nginx/sites-enabled/*; |
Crea un symlink aquí para activar un sitio. |
conf.d/ |
Fragmentos de configuración autoincluidos | Se incluye vía include /etc/nginx/conf.d/*.conf; |
Ajustes globales a nivel http, bloques upstream, maps. |
El patrón sites-available / sites-enabled te permite desactivar un sitio sin borrar su configuración. Para activar un sitio:
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
Para desactivarlo:
rm /etc/nginx/sites-enabled/example.com
Luego recarga:
sudo nginx -t && sudo systemctl reload nginx
Siempre ejecuta nginx -t antes de recargar. Valida la configuración sin afectar el tráfico en curso.
El directorio conf.d/ es más simple. Cada archivo .conf dentro se carga automáticamente. No necesita symlinks. Algunos administradores prefieren conf.d/ por su simplicidad y omiten sites-available/sites-enabled por completo. Ambos enfoques funcionan. Elige uno y mantenlo de forma consistente.
Un detalle importante: si usas tanto conf.d/ como sites-enabled/, asegúrate de no definir bloques server en conflicto en ambos lugares. Nginx cargará ambos, y el resultado depende de las reglas de fusión de directivas y la coincidencia de server_name.
¿Cómo funciona la herencia de directivas en Nginx?
Nginx pasa las directivas de los contextos padre a los contextos hijo. Esta es la parte que la mayoría de la gente entiende mal, y la fuente de errores sutiles en producción.
Hay tres tipos de directivas, y cada una hereda de forma diferente.
Directivas normales
Las directivas normales contienen un único valor. Si un contexto hijo define la misma directiva, reemplaza completamente el valor del padre. Si el hijo no la define, el valor del padre se hereda.
Ejemplo: root es una directiva normal.
http {
root /var/www/default;
server {
server_name example.com;
# root is inherited: /var/www/default
location /app {
root /var/www/app;
# root is overridden: /var/www/app
}
location /blog {
# root is inherited: /var/www/default
}
}
}
Otras directivas normales: index, access_log (cuando se usa una sola vez), error_log, client_max_body_size.
Directivas de tipo array
Las directivas de tipo array (array directives) pueden aparecer múltiples veces en el mismo contexto para acumular valores. Pero cuando un contexto hijo define aunque sea una instancia, reemplaza todos los valores del padre. No los fusiona. Los reemplaza.
add_header es la directiva de tipo array más común. Este comportamiento causa un error de producción muy conocido.
¿Qué pasa cuando agregas headers tanto en el bloque http como en un bloque location?
Si defines add_header en el contexto http y luego defines un add_header diferente en un bloque location, el bloque location elimina todos los headers del contexto http. No solo el que estás sobreescribiendo. Todos.
http {
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
server {
server_name example.com;
location /api/ {
add_header X-Custom "api-response";
# X-Frame-Options is GONE
# X-Content-Type-Options is GONE
# Only X-Custom is sent
}
}
}
Para verificar que esto está ocurriendo en tu servidor:
curl -I https://example.com/api/
Revisa los headers de respuesta. Si tus headers de seguridad faltan en las respuestas de /api/ pero aparecen en otras rutas, esta es la razón.
La solución: repite todos los headers en el contexto hijo.
location /api/ {
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-Custom "api-response";
}
O usa un snippet:
# /etc/nginx/snippets/security-headers.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=63072000" always;
location /api/ {
include snippets/security-headers.conf;
add_header X-Custom "api-response";
}
Nota: Nginx 1.29.3+ introdujo add_header_inherit merge; que cambia este comportamiento. Con merge, los contextos hijo agregan headers a los del padre en lugar de reemplazarlos. Si estás usando Nginx 1.28.x (la versión estable actual a marzo de 2026), aún no tienes esta funcionalidad.
El mismo comportamiento de eliminación aplica a proxy_set_header. Si defines cualquier proxy_set_header en un bloque location, todas las directivas proxy_set_header del contexto server o http se pierden. La documentación oficial lo indica explícitamente: "These directives are inherited from the previous configuration level if and only if there are no proxy_set_header directives defined on the current level."
server {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /api/ {
proxy_set_header X-Request-ID $request_id;
# Host, X-Real-IP, X-Forwarded-For are all GONE
proxy_pass http://backend;
}
}
La solución es la misma: repite todos los headers en el bloque location, o usa un snippet con include.
Directivas de acción
Las directivas de acción como rewrite y return no se heredan en contextos anidados. Solo se ejecutan en el contexto donde están definidas.
server {
rewrite ^/old/(.*)$ /new/$1 permanent;
location /app {
# The rewrite above does NOT apply here
# Requests matching /app are handled by this location
}
}
La directiva try_files tiene una trampa relacionada. Cuando se coloca en el contexto server, Nginx crea una pseudo-location implícita con la prioridad más baja posible. Si cualquier bloque location regular coincide con la petición, try_files a nivel de server nunca se ejecuta:
server {
try_files $uri /index.php; # Never runs for /app/* requests
location /app { } # This catches them first
}
Siempre coloca try_files dentro de un bloque location específico.
¿Cómo inspeccionar la configuración efectiva de Nginx?
Dos comandos te ayudan a entender lo que Nginx realmente ve después de resolver todas las directivas include.
Probar la configuración en busca de errores de sintaxis:
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Siempre ejecuta esto antes de recargar. Si la prueba falla, Nginx te indica el archivo y el número de línea.
Volcar la configuración completa fusionada:
sudo nginx -T
Esto muestra toda la configuración efectiva con todos los includes resueltos y expandidos en línea. Pásalo por less para mayor legibilidad:
sudo nginx -T | less
O busca una directiva específica:
sudo nginx -T | grep -n "add_header"
Esto muestra cada directiva add_header en todos los archivos incluidos, con números de línea en la salida fusionada. Úsalo cuando depures problemas de herencia. Si un header aparece en el bloque http pero no en un bloque location, y no ves ningún add_header en ese location, el header se hereda. Si ves un add_header diferente en el location, todos los headers del padre se eliminan.
Usa nginx -T siempre que los cambios de configuración no se comporten como esperas.
Referencia rápida: el panorama completo
/etc/nginx/nginx.conf
│
├─ main context (process-level: user, worker_processes, pid)
│
├─ events { } (connection handling: worker_connections)
│
└─ http { } (all HTTP config)
│
├─ include conf.d/*.conf (global HTTP settings, upstreams, maps)
├─ include sites-enabled/* (virtual host configs)
│
└─ server { } (one per domain/port)
│
├─ listen, server_name (routing)
├─ root, index (defaults for this host)
│
└─ location { } (URI pattern matching)
├─ try_files, root (per-path behavior)
└─ proxy_pass (reverse proxy target)
Inheritance: parent -> child (down only)
Normal directives: child overrides parent
Array directives: child REPLACES ALL parent values
Action directives: no inheritance
¿Listo para probarlo?
Aloja tus aplicaciones web en un VPS fiable. →