# Nginx

Proxy inverso en dos capas: TLS en el host y server blocks por dominio dentro del VPS, con prueba y vuelta atrás automática.

Fuente: https://ardumaker.com/docs/nginx/

- Categoría: Servidores e infraestructura
- Estado: Disponible
- Requisitos: Un VPS de la empresa, encendido y con host asignado; Ser miembro de la empresa con acceso a ese VPS; Un dominio que apunte al VPS: un subdominio de la zona de ArduMaker o un dominio propio; Un sistema operativo del VPS que soporte la función «nginx del VPS» (se consulta con `ver_funciones_vps`)
- Herramientas MCP (Solo leen): `verificar_dominio_vps`, `listar_dominios_vps`, `ver_nginx_dominio_vps`, `ver_logs_nginx_vps`
- Herramientas MCP (Escriben o modifican): `crear_dominio_vps`, `activar_dominio_vps`
- Herramientas MCP (Destructivas): `editar_nginx_dominio_vps`, `eliminar_dominio_vps`

## ¿Qué permite hacer?

- Publicar un dominio hacia un VPS: el nginx del host compartido termina HTTPS y reenvía el tráfico al nginx que corre dentro del VPS.
- Leer y reescribir el server block de un dominio dentro del VPS, con prueba `nginx -t` y vuelta atrás automática si algo falla.
- Listar los dominios que llegan al VPS, con su `proxy_pass`, la redirección de puerto que usan y el deploy al que están vinculados.
- Leer los logs de acceso y de error de cada dominio (las últimas 500 líneas).
- Borrar un dominio: se quitan su server block del host, el del VPS y, si es de la zona de ArduMaker, su registro DNS.

## ¿Cuándo conviene usarlo?

- Publicar una aplicación propia (Node, Python, Go…) que escucha en un puerto local del VPS.
- Escribir el server block cuando ningún deploy de Git ni ninguna app de ArduMaker lo escribió, es decir, cuando el dominio responde 502 o el sitio por defecto.
- Ajustar el proxy de un sitio: carpetas estáticas, WebSockets, rutas especiales.
- Diagnosticar un 502, una redirección rota o un dominio que no carga.

## ¿Cómo funciona en ArduMaker?

Un dominio pasa por **dos nginx**. El del host compartido termina TLS en el puerto 443 y hace `proxy_pass` a un puerto del host (`host_port`) que es una redirección hacia el puerto 443 del VPS. Adentro del VPS, nginx escucha en el 443 **en HTTP plano** (sin `ssl`) y reparte por `server_name` hacia cada servicio.

### Activar un dominio (lado del host)

1. Se valida que el dominio sea un FQDN estricto (letras, números, guiones y puntos, al menos dos etiquetas). Si no lo es, responde `dominio_invalido` antes de tocar nada.
2. Se rechaza un dominio que ya es de otra empresa (`dominio_ocupado`).
3. Sin `host_port`, el destino es el nginx del VPS: si falta la redirección al 443, se crea cerrada a internet. Con `host_port`, tiene que ser el puerto del host de una redirección de ESTE VPS.
4. Si el nombre no es un subdominio de un solo nivel de la zona de ArduMaker, se comprueba que el DNS llegue al host y, con `create_cert`, se emite el certificado (ver [SSL](https://ardumaker.com/docs/ssl)).
5. Se escribe el server block del host. Con `use_ssl` (por defecto) el puerto 80 redirige a HTTPS con un 301 y el 443 sirve HTTPS con HTTP/2. Se corre `nginx -t` y se recarga; si la prueba falla, lo recién escrito se borra.
6. Si el host ya tiene un server block con ese nombre que apunta a otro VPS, no se toca y la operación falla con `ruta_ocupada`.

### Escribir el server block dentro del VPS

1. Las ediciones del nginx de un mismo VPS se hacen de a una: dos deploys o una app y una edición manual quedan en fila.
2. Si ya existe `/etc/nginx/sites-available/<dominio>.conf`, se guarda una copia `.backup`.
3. Se escribe el contenido completo (reemplaza el archivo, no lo mezcla) y se crea el enlace en `/etc/nginx/sites-enabled/`.
4. Se corre `nginx -t`. Si falla, se borran archivo y enlace, se restaura la copia y se recrea el enlace anterior: la respuesta es `Nginx configuration test failed - changes reverted`.
5. Se reinicia nginx (`systemctl restart nginx`). Si no arranca, se hace la misma vuelta atrás y se responde `Nginx reload failed - changes reverted`.
6. Si todo salió bien, se borra la copia `.backup`.

El contenido no tiene otra validación que `nginx -t`: es el mismo poder que el editor de configuración del panel.

> **INFO:** Activar un dominio **no escribe** el server block del VPS. Lo escribe el deploy de una app de Git que tenga ese dominio, o el editor (`editar_nginx_dominio_vps`). Hasta entonces el dominio responde con el sitio por defecto del VPS o con 502.

## ¿Qué cambia realmente?

### En el host compartido

- Un server block por dominio, que no se ve desde el VPS.
- Si no existía, una redirección de puerto hacia el 443 del VPS. No se abre en el firewall y queda cerrada a los demás VPS del host.

### Dentro del VPS

- `/etc/nginx/sites-available/<dominio>.conf` y su enlace `/etc/nginx/sites-enabled/<dominio>.conf`.
- Los logs `/var/log/nginx/<dominio>_access.log` y `/var/log/nginx/<dominio>_error.log`, si el server block los declara (las plantillas de ArduMaker lo hacen).
- Al borrar el dominio se quitan el archivo y el enlace, y el dominio deja de figurar en los deploys que lo tenían vinculado.

Cada operación queda en el registro de actividad del VPS a nombre de quien la hizo (o de quien autorizó al agente).

## Puertos y proxy

| Tramo | Puerto | Protocolo | Notas |
| --- | --- | --- | --- |
| Internet → host | 80 / 443 | HTTP / HTTPS (TLS 1.2 y 1.3, HTTP/2) | Con `use_ssl`, el 80 responde 301 a HTTPS |
| Host → VPS | `host_port` | HTTP plano | Redirección de puerto asignada sola; no es pública |
| nginx del VPS | 443 | HTTP plano | `listen 443;` sin `ssl`; reparte por `server_name` |
| nginx del VPS → servicio | puerto del servicio | HTTP | Lo define el `proxy_pass` (normalmente `127.0.0.1:<puerto>`) |

### Lo que agrega el proxy del host

- Cabeceras `Host`, `X-Real-IP` (la IP del cliente, siempre sobrescrita), `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host` y `X-Forwarded-Port`.
- Soporte de WebSocket (`Upgrade` y `Connection`).
- `client_max_body_size 100M` y tiempos de conexión, envío y lectura de 360 segundos.

El nginx del VPS toma la IP del cliente de `X-Real-IP` solo cuando la conexión llega por loopback, que es como llega el proxy del host. Una app detrás de él ve la IP real en `X-Real-IP`.

## Ejemplo de server block

Un server block mínimo para una app que escucha en el puerto 3000 del VPS:

```nginx
server {
    listen 443;
    listen [::]:443;
    server_name app.ejemplo.com;

    access_log /var/log/nginx/app.ejemplo.com_access.log;
    error_log /var/log/nginx/app.ejemplo.com_error.log;

    absolute_redirect off;

    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_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

- Sin `ssl` ni certificados: el TLS termina en el host y los certificados no se copian al VPS.
- `absolute_redirect off;` hace que las redirecciones propias de nginx (por ejemplo `/blog` → `/blog/`) salgan relativas. Sin eso salen como `http://dominio:443/blog/` y el navegador no llega.
- Los logs con el nombre `<dominio>_access.log` y `<dominio>_error.log` son los que lee `ver_logs_nginx_vps`.

## Conflictos y concurrencia

- `dominio_ocupado` (409): el dominio es de otra empresa (cae en una zona que registró otra empresa, otra lo publicó con Apps o un deploy de otra lo tiene vinculado).
- `ruta_ocupada`: el host ya tiene un server block de ese nombre que apunta a otro VPS. No se toca.
- `host_port_no_es_del_vps` (400): el `host_port` enviado no es una redirección de este VPS. La respuesta trae `valid_host_ports` y `vps_nginx_host_port`. `host_port` **no** es el puerto donde escucha el servicio dentro del VPS.
- Dos ediciones simultáneas del nginx de un mismo VPS no se pisan: se ejecutan una detrás de otra.
- Un dominio que ya usa una app o n8n figura como usado en `listar_dominios_vps` (campos `used_by`, `linked_to_repo` y `puerto_in_vps`).

## Esquema

**Cómo llega una visita a tu servicio**

```text
Navegador (pide https://app.ejemplo.com)
  ↓
Cloudflare (solo subdominios de la zona de ArduMaker, con proxy)
  ↓
nginx del host :443 (TLS y certificado; proxy_pass al host_port)
  ↓
Redirección de puerto (host_port → 443 del VPS, cerrada a internet)
  ↓
nginx del VPS :443 (HTTP plano; elige el server block por server_name)
  ↓
Servicio (127.0.0.1:<puerto> dentro del VPS)
```

Con un dominio propio sin proxy, la visita llega directo al host. Si al activar se elige un `host_port` de una redirección a otro puerto, el tráfico salta el nginx del VPS (`via: direct`).

## Resultado esperado

- `activar_dominio_vps` devuelve `nginx` (`success`, `domain`, `host_port`, `exit_code`), `certificate` y `target` (`host_port`, `vps_port`, `via`, `next_step`).
- `via: "vps_nginx"`: el dominio llega al nginx del VPS y necesita su server block. `via: "direct"`: va directo a ese puerto del VPS, que tiene que responder HTTP plano.
- `listar_dominios_vps` muestra cada dominio con `server_name`, `proxy_pass`, `puerto`, `puerto_in_vps` y `linked_to_repo`.

Para comprobarlo desde afuera:

```bash
curl -sI https://app.ejemplo.com/
# una carpeta debe redirigir de forma relativa: Location: /blog/
curl -sI https://app.ejemplo.com/blog
```

## Errores y resolución

| Síntoma | Causa | Qué hacer |
| --- | --- | --- |
| 502 o el sitio por defecto después de activar | No hay server block en el VPS, o el servicio no escucha en el puerto del `proxy_pass` | Escribir el server block; revisar el log de error del dominio y el estado del servicio |
| `Nginx configuration test failed - changes reverted` | `nginx -t` rechazó la configuración; quedó la versión anterior | Corregir el contenido (viene en `nginx_test_error`) y reenviar |
| `Nginx reload failed - changes reverted` | nginx no pudo reiniciar, por ejemplo por un puerto ocupado | Leer `ver_logs_systemd_vps` con el servicio `nginx` |
| `nginx_invalido` | El nginx del host rechazó la configuración del dominio; no se publicó nada. El detalle queda en el log del servidor | Reintentar; si se repite, reportarlo |
| `certificado_del_host_ausente` | El host no tiene certificado para ese dominio | Activar con `create_cert=true`, o con `use_ssl=false` |
| `redireccion_fallida` | El host no pudo abrir el puerto hacia el nginx del VPS | Reintentar |
| `Domain is not pointing to the host IP` / `Domain could not be resolved` | El DNS todavía no apunta al VPS | Revisar con `verificar_dominio_vps` y esperar la propagación |
| Una carpeta redirige a `http://dominio:443/...` | El server block arma redirecciones absolutas | Agregar `absolute_redirect off;` |
| `Access log file not found for domain` | El server block no declara un log con el nombre esperado | Declarar `access_log` y `error_log` como en el ejemplo |
| `Config file not found or unreadable` | Ese dominio no tiene server block en el VPS | Crearlo con `editar_nginx_dominio_vps` |

Si el host o el VPS no contestan, la respuesta es un error de estado (424), reintentable. `activar_dominio_vps` y `editar_nginx_dominio_vps` son seguros de repetir: el primero pregunta antes de crear cada pieza y el segundo reemplaza el archivo completo. `eliminar_dominio_vps` pide cuidado: borra el registro DNS de la zona de ArduMaker y los dos server blocks.

## Relación con MCP

Sí, hay acceso MCP. Las herramientas están en los perfiles `/mcp` y `/mcp/vps` y actúan con los permisos de la persona que autorizó al agente; un agente de solo lectura solo ve las de lectura.

| Herramienta | Tipo | Qué hace |
| --- | --- | --- |
| `verificar_dominio_vps` | Lectura | Comprueba si el DNS ya llega al host del VPS |
| `listar_dominios_vps` | Lectura | Dominios del VPS y su destino |
| `ver_nginx_dominio_vps` | Lectura | Lee el server block del VPS |
| `ver_logs_nginx_vps` | Lectura | Logs de acceso o error; llegan marcados como contenido no confiable |
| `crear_dominio_vps` | Escritura | Registro DNS de la zona de ArduMaker (acepta `idempotency_key`) |
| `activar_dominio_vps` | Escritura | Publica en el host; devuelve un `operacion_id` para `ver_operacion` |
| `editar_nginx_dominio_vps` | Destructiva | Reemplaza el server block completo y reinicia nginx |
| `eliminar_dominio_vps` | Destructiva | Borra DNS de la zona y ambos server blocks |

Secuencia típica: `crear_dominio_vps` (solo para la zona de ArduMaker) → `verificar_dominio_vps` → `activar_dominio_vps` sin `host_port` → `editar_nginx_dominio_vps` si ningún deploy escribe el server block. Referencia de cada herramienta en [/mcp/docs](https://ardumaker.com/mcp/docs) y ejemplos en [/mcp/dominios](https://ardumaker.com/mcp/dominios).

## Funciones relacionadas

- [Certificados SSL](https://ardumaker.com/docs/ssl/)
- [Dominios y DNS](https://ardumaker.com/docs/dominios-y-dns/)
- [Firewall y puertos](https://ardumaker.com/docs/firewall-y-puertos/)
- [Deploy desde GitHub](https://ardumaker.com/docs/github-deploy/)
- [Servidores VPS](https://ardumaker.com/docs/vps/)
- [Monitorización](https://ardumaker.com/docs/monitorizacion/)
