Estructura de los archivos de configuracion de Nginx

10 min de lectura·Matthieu|

Una guia completa sobre como se organizan los archivos de configuracion de Nginx en disco, como se anidan los contextos entre si, como las directivas include cargan archivos externos y como funciona realmente la herencia de directivas.

Estructura de los archivos de configuracion de Nginx

Instalaste Nginx. Abriste /etc/nginx/nginx.conf y encontraste un archivo que incluye otros archivos, que a su vez incluyen mas archivos. Algunas directivas estan dentro de llaves, otras flotan en el nivel superior. Antes de configurar server blocks, SSL o reverse proxies, necesitas el mapa.

Este articulo te da ese mapa. Sin recetas. Solo el modelo mental de como funciona la configuracion de Nginx, para que puedas leer y modificar cualquier configuracion que encuentres.

Donde se almacenan los archivos de configuracion de Nginx?

En Debian 12 y Ubuntu 24.04, el paquete apt instala todo bajo /etc/nginx/. Asi se ve ese directorio en una instalacion 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 mas frecuencia no es nginx.conf en si. 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 traves de directivas include.

Como se organizan los archivos de configuracion de Nginx?

La configuracion de Nginx usa un arbol 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 jerarquia se ve asi:

main (top level, outside any braces)
├── events { }
└── http { }
    └── server { }
        └── location { }

Cada directiva en la configuracion vive en uno de estos niveles. El contexto main es el archivo en si. Todo lo demas se anida dentro.

Que controla el contexto main?

El contexto main es todo lo que esta 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 Proposito Valor tipico
user Usuario del SO bajo el que corren los worker processes www-data
worker_processes Numero de worker processes auto (coincide con nucleos 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 modulos /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 lineas se ejecutan antes que todo lo demas. Establecen bajo que usuario corre Nginx, cuantos workers inicia y donde escribe los errores.

Que va en el bloque events?

El bloque events configura como 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 numero maximo de conexiones simultaneas por worker process. Con worker_processes auto en una maquina de 4 nucleos, obtienes 4 x 768 = 3,072 conexiones concurrentes. El valor por defecto del codigo fuente de Nginx es 512, pero Debian lo establece en 768.

Para que sirve el contexto http?

El bloque http contiene toda la configuracion relacionada con el manejo de trafico HTTP. Cada bloque server vive dentro de el. Las directivas que coloques aqui 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 numero de version de Nginx en los headers de respuesta. Revelar la version ayuda a los atacantes a apuntar a vulnerabilidades conocidas. El valor por defecto de Debian no incluye esta linea. Agregala.

Las dos lineas include al final son la forma en que Nginx carga las configuraciones reales de tus sitios. Cubriremos include en detalle mas adelante.

Como funcionan los bloques server?

Un bloque server define un virtual host. Vive dentro del contexto http. Cada bloque server escucha en una combinacion de direccion/puerto y asocia peticiones segun 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 peticion, Nginx elige el bloque server comparando server_name con el header Host. Si ningun 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).

Como resuelve Nginx los bloques location?

Un bloque location define como Nginx maneja las peticiones para patrones de URI especificos. Vive dentro de un bloque server (o dentro de otro location).

Nginx evalua los bloques location en un orden especifico:

Modificador Tipo Ejemplo Comportamiento
= Coincidencia exacta location = / Coincide solo con /. Detiene la busqueda inmediatamente.
^~ Prefijo prioritario location ^~ /images/ Coincide por prefijo. Omite la verificacion de regex.
~ Regex (distingue mayusculas) location ~ \.php$ La primera coincidencia regex gana.
~* Regex (no distingue mayusculas) location ~* \.(jpg|png)$ La primera coincidencia regex gana.
(ninguno) Prefijo location /api/ El prefijo mas largo gana, pero un regex puede sobreescribirlo.

El algoritmo de coincidencia:

  1. Nginx verifica todas las locations de prefijo y recuerda la coincidencia mas larga.
  2. Si esa coincidencia usa = o ^~, se detiene. La usa.
  3. De lo contrario, verifica las locations regex en el orden en que aparecen en el archivo de configuracion.
  4. La primera coincidencia regex gana. Si ninguna regex coincide, usa el prefijo mas largo del paso 1.

Esto significa que el orden de las locations de prefijo en tu archivo de configuracion no importa. Pero el orden de las regex si.

Como funciona la directiva include?

La directiva include inserta el contenido de otro archivo (o archivos que coincidan con un patron glob) en la posicion actual de la configuracion. Nginx la resuelve al momento de cargar la configuracion, antes de que se analice como un todo.

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

Asi 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 esta vacio, Nginx arranca sin problemas (los globs pueden no coincidir con nada). Pero include /etc/nginx/ssl.conf; fallara si ese archivo no existe.

Cual es la diferencia entre sites-available, sites-enabled y conf.d?

Estos tres directorios tienen propositos diferentes. Debian y Ubuntu usan los tres.

Directorio Proposito Como lo lee Nginx Cuando 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 via include /etc/nginx/sites-enabled/*; Crea un symlink aqui para activar un sitio.
conf.d/ Fragmentos de configuracion autoincluidos Se incluye via include /etc/nginx/conf.d/*.conf; Ajustes globales a nivel http, bloques upstream, maps.

El patron sites-available / sites-enabled te permite desactivar un sitio sin borrar su configuracion. 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 configuracion sin afectar el trafico en curso.

El directorio conf.d/ es mas simple. Cada archivo .conf dentro se carga automaticamente. 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/, asegurate de no definir bloques server en conflicto en ambos lugares. Nginx cargara ambos, y el resultado depende de las reglas de fusion de directivas y la coincidencia de server_name.

Como 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 mayoria de la gente entiende mal, y la fuente de errores sutiles en produccion.

Hay tres tipos de directivas, y cada una hereda de forma diferente.

Directivas normales

Las directivas normales contienen un unico 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 multiples 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 mas comun. Este comportamiento causa un error de produccion muy conocido.

Que 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 estas 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 esta 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 razon.

La solucion: 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 estas usando Nginx 1.28.x (la version estable actual a marzo de 2026), aun no tienes esta funcionalidad.

El mismo comportamiento de eliminacion 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 documentacion oficial lo indica explicitamente: "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 solucion es la misma: repite todos los headers en el bloque location, o usa un snippet con include.

Directivas de accion

Las directivas de accion como rewrite y return no se heredan en contextos anidados. Solo se ejecutan en el contexto donde estan 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 implicita con la prioridad mas baja posible. Si cualquier bloque location regular coincide con la peticion, 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 especifico.

Como inspeccionar la configuracion efectiva de Nginx?

Dos comandos te ayudan a entender lo que Nginx realmente ve despues de resolver todas las directivas include.

Probar la configuracion 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 numero de linea.

Volcar la configuracion completa fusionada:

sudo nginx -T

Esto muestra toda la configuracion efectiva con todos los includes resueltos y expandidos en linea. Pasalo por less para mayor legibilidad:

sudo nginx -T | less

O busca una directiva especifica:

sudo nginx -T | grep -n "add_header"

Esto muestra cada directiva add_header en todos los archivos incluidos, con numeros de linea en la salida fusionada. Usalo cuando depures problemas de herencia. Si un header aparece en el bloque http pero no en un bloque location, y no ves ningun 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 configuracion no se comporten como esperas.

Referencia rapida: 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

Copyright 2026 Virtua.Cloud. Todos los derechos reservados. Este contenido es una obra original del equipo de Virtua.Cloud. La reproduccion, republicacion o redistribucion sin permiso escrito esta prohibida.

¿Listo para probarlo?

Despliega tu propio servidor en segundos. Linux, Windows o FreeBSD.

Ver planes VPS