Skill: ivangdavila/nginx (version 1.0.1)

--- name: Nginx slug: nginx version: 1.0.1 description: Configure Nginx for reverse proxy, load balancing, SSL termination, and high-performance static serving. --- ## When to Use User needs Nginx expertise — from basic server blocks to production configurations. Agent handles reverse proxy, SSL, caching, and performance tuning. ## Quick Reference | Topic | File | |-------|------| | Reverse proxy patterns | `proxy.md` | | SSL/TLS configuration | `ssl.md` | | Performance tuning | `performance.md` | | Common configurations | `examples.md` | ## Location Matching - Exact `=` first, then `^~` prefix, then regex `~`/`~*`, then longest prefix - `location /api` matches `/api`, `/api/`, `/api/anything` — prefix match - `location = /api` only matches exactly `/api` — not `/api/` - `location ~ \.php$` is regex, case-sensitive — `~*` for case-insensitive - `^~` stops regex search if prefix matches — use for static files ## proxy_pass Trailing Slash - `proxy_pass http://backend` preserves location path — `/api/users` → `/api/users` - `proxy_pass http://backend/` replaces location path — `/api/users` → `/users` - Common mistake: missing slash = double path — or unexpected routing - Test with `curl -v` to see actual backend request ## try_files - `try_files $uri $uri/ /index.html` for SPA — checks file, then dir, then fallback - Last argument is internal redirect — or `=404` for error - `$uri/` tries directory with index — set `index index.html` - Don't use for proxied locations — use `proxy_pass` directly ## Proxy Headers - `proxy_set_header Host $host` — backend sees original host, not proxy IP - `proxy_set_header X-Real-IP $remote_addr` — client IP, not proxy - `proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for` — append to chain - `proxy_set_header X-Forwarded-Proto $scheme` — for HTTPS detection ## Upstream - Define servers in `upstream` block — `upstream backend { server 127.0.0.1:3000; }` - `proxy_pass http://backend` uses upstream — load balancing included - Health checks with `max_fails` and `fail_timeout` — marks server unavailable - `keepalive 32` for connection pooling — reduces connection overhead ## SSL/TLS - `ssl_certificate` is full chain — cert + intermediates, not just cert - `ssl_certificate_key` is private key — keep permissions restricted - `ssl_protocols TLSv1.2 TLSv1.3` — disable older protocols - `ssl_prefer_server_ciphers on` — server chooses cipher, not client ## Common Mistakes - `nginx -t` before `nginx -s reload` — test config first - Missing semicolon — syntax error, vague message - `root` inside `location` — prefer in `server`, override only when needed - `alias` vs `root` — alias replaces location, root appends location - Variables in `if` — many things break inside if, avoid complex logic ## Variables - `$uri` is decoded, normalized path — `/foo%20bar` becomes `/foo bar` - `$request_uri` is original with query string — unchanged from client - `$args` is query string — `$arg_name` for specific parameter - `$host` from Host header — `$server_name` from config ## Performance - `worker_processes auto` — matches CPU cores - `worker_connections 1024` — per worker, multiply by workers for max - `sendfile on` — kernel-level file transfer - `gzip on` only for text — `gzip_types text/plain application/json ...` - `gzip_min_length 1000` — small files not worth compressing ## Logging - `access_log off` for static assets — reduces I/O - Custom log format with `log_format` — add response time, upstream time - `error_log` level: `debug`, `info`, `warn`, `error` — debug is verbose - Conditional logging with `map` and `if` — skip health checks

path: examples.md
content: # Configuration Traps

## Location Matching

- `location /api` matchea `/api`, `/api/`, `/api-v2`, `/apiary` — más amplio de lo esperado
- `location /api/` (con slash) NO matchea `/api` sin slash
- `location = /api` es exacto pero no matchea `/api/`
- Regex `location ~` tiene prioridad sobre prefix más largo — orden importa

## Root vs Alias

- `root /var/www; location /img/` → busca en `/var/www/img/`
- `alias /var/www/; location /img/` → busca en `/var/www/` (reemplaza location)
- `alias` sin trailing slash + location con slash = path mal formado
- `alias` con regex requiere captura: `alias /var/www$1`

## try_files

- `try_files $uri /index.html` sin `$uri/` — no encuentra directorios
- Último argumento es internal redirect, no file check — comportamiento diferente
- `try_files` + `proxy_pass` en mismo location = try_files siempre gana
- `=404` como fallback es código, no archivo — `/404` sería archivo

## If Statement

- `if` crea nuevo context — directivas heredadas pueden no aplicar
- `if ($request_uri ~* \.php)` en location ya procesada = doble check inútil
- `return` y `rewrite` dentro de `if` funcionan — otras directivas problemáticas
- Múltiples `if` no se combinan con AND — cada uno es independiente

## Variables

- `$uri` está normalizado (decoded) — `$request_uri` es raw
- Variable undefined = empty string, no error
- `set` dentro de `if` siempre ejecuta — solo el bloque es condicional
- `map` es más eficiente que múltiples `if` para switch/case

## Includes

- `include /etc/nginx/conf.d/*.conf` — orden alfabético, puede importar
- Include de archivo inexistente = nginx no arranca
- Include relativo es relativo a nginx.conf, no al archivo actual
- Circular includes = nginx no arranca con error confuso


path: performance.md
content: # Performance Traps

## Worker Configuration

- `worker_processes auto` puede ser excesivo en containers — limitar manualmente
- `worker_connections 1024` es por worker — total = workers × connections
- `multi_accept on` puede causar starvation en algunos workers
- CPU pinning (`worker_cpu_affinity`) rara vez ayuda en práctica

## Buffers

- `client_body_buffer_size` pequeño + uploads = escritura a disco tmp
- Buffer overflow silencioso — request se procesa pero más lento
- `proxy_buffer_size` debe ser suficiente para headers de respuesta
- Muchos buffers pequeños peor que pocos grandes para memoria

## Gzip

- `gzip on` sin `gzip_types` = solo text/html comprimido
- Gzip en archivos ya comprimidos (jpg, png, zip) = CPU waste + archivo más grande
- `gzip_vary on` crítico para CDN — sin él, CDN puede servir versión wrong
- `gzip_comp_level 9` rara vez vale la pena — 6 es sweet spot

## Caching

- `proxy_cache` sin `proxy_cache_key` usa default que puede colisionar
- Cache key con `$request_uri` incluye query string — puede explotar caché
- `proxy_cache_valid 200 1h` — pero 301/302 se cachean para siempre por defecto
- `inactive` vs `max_size` — inactive borra por tiempo, max_size por espacio

## Static Files

- `sendfile on` sin `tcp_nopush on` = no aprovecha kernel optimization
- `expires` header sin `Cache-Control` = browsers ignoran
- `try_files` con muchos paths = múltiples syscalls
- Symlinks + `disable_symlinks` por seguridad puede romper deploys

## Logging

- `access_log` sync por defecto = I/O block en cada request
- `buffer=32k flush=5s` para async logging — pero puedes perder logs en crash
- Log rotation sin `reopen` signal = nginx escribe a archivo borrado
- JSON logging parseable pero más bytes por línea


path: proxy.md
content: # Proxy Traps

## proxy_pass URL

- `proxy_pass http://backend` (sin slash) — preserva `/api/users` → `/api/users`
- `proxy_pass http://backend/` (con slash) — reemplaza `/api/users` → `/users`
- Mezclar `location /api/` con `proxy_pass http://x/v1` = paths inesperados
- Variables en proxy_pass (`$uri`) cambian completamente el comportamiento

## Headers

- `proxy_set_header Host $host` — sin esto, backend recibe IP del proxy como Host
- `Host $http_host` incluye puerto — `Host $host` no
- `X-Forwarded-For` se sobreescribe, no se añade — usar `$proxy_add_x_forwarded_for`
- Headers con underscore `_` ignorados por defecto — `underscores_in_headers on` para permitir

## WebSocket

- Falta `proxy_http_version 1.1` = WebSocket falla silenciosamente
- `Connection "upgrade"` debe ser literal string, no variable
- Timeout por defecto 60s mata conexiones WebSocket idle — subir `proxy_read_timeout`
- Múltiples proxies en cadena = cada uno necesita upgrade headers

## Buffering

- `proxy_buffering on` (default) — respuesta completa antes de enviar a client
- Con buffering, respuestas streaming no funcionan — SSE, chunked encoding rotos
- `X-Accel-Buffering: no` header del backend puede desactivar — pero no siempre funciona
- Buffer muy pequeño + respuesta grande = escritura a disco temporal

## Timeouts

- `proxy_connect_timeout` default 60s — muy largo para detectar backend caído
- Backend lento + `proxy_read_timeout` bajo = 504 frecuentes
- Timeout en nginx no cancela request en backend — sigue procesando

## Upstream

- Upstream server sin puerto = puerto 80 implícito
- `max_fails=0` desactiva health checks — server never marked down
- `fail_timeout` es DOBLE: período de conteo Y tiempo de ban
- Round-robin ignora peso si solo hay 1 server up


path: ssl.md
content: # SSL Traps

## Certificates

- `ssl_certificate` debe ser fullchain (cert + intermediate) — solo cert = warning en browsers
- Orden del chain importa: cert primero, luego intermediates, NO root
- Key y cert mismatch = nginx no arranca con error críptico
- Cert expirado = nginx arranca OK pero browsers rechazan

## Configuration

- `ssl on` está deprecated — usar `listen 443 ssl` en su lugar
- `ssl_protocols TLSv1 TLSv1.1` = inseguro — mínimo TLSv1.2
- `ssl_prefer_server_ciphers on` ya no recomendado para TLS 1.3 — solo para 1.2
- `ssl_ciphers` mal ordenados = servidor elige cipher débil

## OCSP Stapling

- `ssl_stapling on` sin `ssl_trusted_certificate` = stapling silenciosamente desactivado
- DNS resolver necesario para OCSP — `resolver 8.8.8.8` si no hay local
- OCSP de algunas CAs es lento/unreliable — puede añadir latencia

## HTTP/2

- `http2` directive en listen, no como módulo separado
- `http2_push` deprecated en nginx 1.25+
- HTTP/2 + proxy a HTTP/1.1 backend funciona — pero pierde multiplexing
- `large_client_header_buffers` puede necesitar ajuste para HTTP/2

## Client Certificates

- `ssl_verify_client on` rechaza sin cert — usar `optional` para hacer opcional
- `ssl_client_certificate` es la CA, no el cert del cliente
- `$ssl_client_verify` es "SUCCESS", no "true" o "1"
- CRL checking requiere config adicional — sin ella certs revocados son aceptados

## Common Mistakes

- Redirect loop: 80→443→80 porque `X-Forwarded-Proto` no se chequea
- Mixed content: HTTPS página carga HTTP recursos — `upgrade-insecure-requests` ayuda
- HSTS con max-age muy largo + certificado inválido = sitio inaccesible
- Cert para `example.com` no cubre `www.example.com` — necesita SAN o wildcard


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)