Nginx como Reverse Proxy em 2026: Como Configurar SSL, Load Balancing e Caching para PME?
✎ Duarte Spínola |
O Nginx é hoje o servidor web e reverse proxy mais utilizado na Internet, servindo mais de um terço de todos os sites ativos (Netcraft Web Server Survey, 2025). Para uma pequena ou média empresa (PME) que opera aplicações web, API REST, ou microserviços, o Nginx funciona como ponto único de entrada: termina ligações SSL/TLS, distribui tráfego entre vários backends, faz cache de conteúdo estático e aplica políticas de segurança. Este artigo cobre a instalação em Debian/Ubuntu e RHEL, configuração completa de reverse proxy com certificados Let’s Encrypt, estratégias de load balancing, health checks, caching, headers de segurança, rate limiting, WebSocket proxying, tuning de performance, troubleshooting e comparação com alternativas modernas como Traefik e Caddy.
1. Instalação do Nginx em Debian, Ubuntu e RHEL
A instalação varia conforme a distribuição. Em Debian e Ubuntu, o Nginx está disponível nos repositórios oficiais, mas a versão pode ser antiga. Para obter a versão estável mais recente, recomenda-se o repositório oficial do Nginx (nginx.org).
Debian 12 (Bookworm) e Ubuntu 24.04 LTS:
sudo apt update
sudo apt install -y curl gnupg2 ca-certificates lsb-release debian-archive-keyring
# Importar a chave GPG oficial do Nginx
curl https://nginx.org/keys/nginx_signing.key | gpg –dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
# Adicionar o repositório estável
echo “deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/debian $(lsb_release -cs) nginx” \
| sudo tee /etc/apt/sources.list.d/nginx.list
# Instalar
sudo apt update
sudo apt install -y nginx
# Verificar versão
nginx -v
RHEL 9 / AlmaLinux 9 / Rocky Linux 9:
sudo dnf install -y epel-release
# Adicionar repositório oficial do Nginx
sudo tee /etc/yum.repos.d/nginx.repo << ‘EOF’
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF
# Instalar
sudo dnf install -y nginx
# Activar e iniciar o serviço
sudo systemctl enable –now nginx
Após a instalação, confirmar que o serviço está a correr:
curl -I http://localhost
O directório principal de configuração é /etc/nginx/, onde nginx.conf é o ficheiro de configuração global e conf.d/ contém os ficheiros de virtual hosts. No pacote oficial do Nginx (não no do Debian), os sites vão em /etc/nginx/conf.d/ com extensão .conf (nginx.org — Beginner’s Guide).
2. Configuração de Reverse Proxy com SSL/TLS via Let’s Encrypt
A configuração de um reverse proxy com SSL/TLS envolve dois passos: obter o certificado via Certbot e configurar o Nginx para terminar SSL e reencaminhar tráfego para o backend.
Instalar o Certbot (certbot.eff.org):
sudo apt install -y certbot python3-certbot-nginx
# RHEL
sudo dnf install -y certbot python3-certbot-nginx
Configuração base do reverse proxy (/etc/nginx/conf.d/app.example.com.conf):
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
# Reverse proxy com SSL
server {
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
location / {
proxy_pass http://127.0.0.1:8080;
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_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
}
}
Obter o certificado:
O Certbot configura automaticamente a renovação via systemd timer. Testar a renovação com sudo certbot renew --dry-run (certbot.eff.org — Instructions).
O parâmetro proxy_set_header X-Forwarded-Proto $scheme; é essencial para que o backend saiba que a ligação original era HTTPS. Sem isto, aplicações podem gerar URLs http:// em redirects, quebrando ligações seguras (nginx.org — proxy_set_header).
3. Load Balancing: Round-Robin, Least Connections e IP Hash
O Nginx Open Source suporta três métodos de load balancing no módulo ngx_http_upstream_module (nginx.org — ngx_http_upstream_module):
| Método | Directiva | Quando Usar |
|---|---|---|
| Round-robin (predefinido) | Nenhuma (comportamento padrão) | Backends homogéneos, tráfego uniforme |
| Least connections | least_conn; |
Backends com tempos de resposta variáveis |
| IP hash | ip_hash; |
Sticky sessions baseadas em IP do cliente |
Configuração round-robin (predefinido):
server 10.0.0.11:8080;
server 10.0.0.12:8080;
server 10.0.0.13:8080;
}
Least connections — distribui para o backend com menos ligações activas:
least_conn;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
server 10.0.0.13:8080;
}
IP hash — mantém o mesmo cliente sempre no mesmo backend (session affinity):
ip_hash;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
server 10.0.0.13:8080;
}
Backups e pesos — priorizar servidores mais potentes e manter fallback:
server 10.0.0.11:8080 weight=3;
server 10.0.0.12:8080 weight=2;
server 10.0.0.13:8080 weight=1;
server 10.0.0.99:8080 backup;
}
O parâmetro weight=3 significa que o primeiro servidor recebe três vezes mais pedidos do que o terceiro. O servidor marcado como backup só recebe tráfego quando todos os outros estão indisponíveis (nginx.org — Server Parameters).
Para session affinity sem IP hash (que quebra quando o IP do cliente muda, como em redes móveis), a alternativa no Open Source é usar o módulo sticky via nginx-module-sticky de terceiros ou implementar consistent hashing com hash $request_uri;.
4. Health Checks: Nginx Plus vs Workarounds Open Source
O Nginx Plus (versão comercial) inclui health checks activos que sondam os backends periodicamente e removem automaticamente servidores que falham (nginx.com — Health Checks):
upstream backend_app {
zone backend_app 64k;
server 10.0.0.11:8080;
server 10.0.0.12:8080;
health_check interval=10s fails=3 passes=2;
health_check uri=/health;
}
No Nginx Open Source, não existem health checks activos nativos. A directiva proxy_next_upstream é o workaround padrão: se um backend retornar erro (502, 503, 504) ou timeout, o Nginx tenta o próximo servidor (nginx.org — proxy_next_upstream):
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
}
server {
location / {
proxy_pass http://backend_app;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
}
}
O max_fails=3 fail_timeout=30s significa que se um servidor falhar 3 vezes em 30 segundos, é marcado como indisponível durante 30 segundos. Não é um health check activo, mas detecta backends indisponíveis sem custo adicional.
Workaround com script externo (Open Source): Para health checks activos sem Nginx Plus, uma abordagem comum é usar um cron job ou um serviço externo (Consul, HAProxy, ou um script bash com curl) que sonda os backends e actualiza dinamicamente a configuração via nginx -s reload ou via API do módulo upstream dynamic.
# Health check simples — adicionar a um cron a cada minuto
BACKENDS=”10.0.0.11 10.0.0.12″
ACTIVE=””
for ip in $BACKENDS; do
if curl -sf -o /dev/null –max-time 2 “http://$ip:8080/health”; then
ACTIVE=”$ACTIVE server $ip:8080;”
fi
done
# Reescrever upstream e fazer reload se mudou
echo “upstream backend_app { $ACTIVE }” > /etc/nginx/conf.d/upstream_dynamic.conf
nginx -t && nginx -s reload
Esta abordagem é funcional mas tem limitações: durante a janela entre falhas e detecção, pedidos podem ir para backends indisponíveis. Para infraestruturas críticas, o Nginx Plus ou um load balancer dedicado (HAProxy) é mais adequado.
5. Caching de Conteúdo Estático
O módulo ngx_http_proxy_module inclui caching integrado que armazena respostas do backend em disco ou memória (nginx.org — proxy_cache):
http {
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=app_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# Cache para conteúdo estático
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2?|ttf|eot)$ {
proxy_cache app_cache;
proxy_cache_valid 200 304 12h;
proxy_cache_valid 404 1m;
proxy_cache_key $scheme$request_method$host$request_uri;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend_app;
}
# Cache para API com TTL curto
location /api/public/ {
proxy_cache app_cache;
proxy_cache_valid 200 5m;
proxy_cache_valid 404 1m;
proxy_cache_key $scheme$request_method$host$request_uri;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend_app;
}
# Sem cache para conteúdo dinâmico
location / {
proxy_pass http://backend_app;
}
}
}
O parâmetro proxy_cache_use_stale permite servir cache expirado quando o backend está indisponível, aumentando a resiliência. O header X-Cache-Status mostra se o conteúdo veio do cache (HIT), do backend (MISS), estava expirado (EXPIRED), ou é uma actualização em segundo plano (UPDATING).
O directório de cache precisa de permissões correctas:
sudo chown nginx:nginx /var/cache/nginx
6. Headers de Segurança: HSTS, X-Frame-Options, CSP
A aplicação de headers de segurança HTTP é uma camada defensiva essencial. O Nginx permite configurá-los via add_header (nginx.org — add_header):
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# HSTS — forçar HTTPS durante 1 ano, com preload
add_header Strict-Transport-Security “max-age=31536000; includeSubDomains; preload” always;
# Prevenir clickjacking
add_header X-Frame-Options “SAMEORIGIN” always;
# MIME sniffing protection
add_header X-Content-Type-Options “nosniff” always;
# Content Security Policy — ajustar conforme a aplicação
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ ‘unsafe-inline’; style-src ‘self’ ‘unsafe-inline’; img-src ‘self’ data: https:; font-src ‘self’; connect-src ‘self’; frame-ancestors ‘self’;” always;
# Referer policy
add_header Referrer-Policy “strict-origin-when-cross-origin” always;
# Permissions policy
add_header Permissions-Policy “geolocation=(), microphone=(), camera=()” always;
location / {
proxy_pass http://backend_app;
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_set_header X-Forwarded-Proto $scheme;
}
}
O sufixo always garante que o header é enviado mesmo em respostas de erro (4xx, 5xx), não apenas em 200 OK. Sem always, o Nginx omite headers em respostas de erro, o que abre uma brecha (OWASP Secure Headers Project).
Para HSTS com preload, o domínio deve ser submetido em hstspreload.org. Após aceitação, todos os navegadores passam a forçar HTTPS permanentemente — não usar preload sem certeza de que todos os subdomínios suportam HTTPS.
7. Rate Limiting e Prevenção de Abuso
O Nginx implementa rate limiting via módulo ngx_http_limit_req_module (nginx.org — limit_req):
# Zona de rate limiting: 10 pedidos por segundo por IP
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# Zona de rate limiting para login: 1 pedido por segundo
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;
# Zona de limite de ligações simultâneas
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# Limite de ligações simultâneas por IP
limit_conn conn_limit 20;
# API — burst controlado
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
limit_req_log_level warn;
proxy_pass http://backend_app;
}
# Login — mais restritivo
location /login {
limit_req zone=login_limit burst=5 nodelay;
limit_req_status 429;
proxy_pass http://backend_app;
}
location / {
proxy_pass http://backend_app;
}
}
}
O parâmetro burst=20 nodelay permite 20 pedidos em rajada sem atraso antes de aplicar o limite de 10 por segundo. Sem nodelay, os pedidos em excesso são colocados em fila e respondidos com atraso, o que pode causar timeouts no cliente (nginx.org — limit_req).
A directiva limit_req_status 429 retorna HTTP 429 Too Many Requests, o código correcto para rate limiting. Por defeito, o Nginx retorna 503, que é semanticamente incorrecto.
Para limitar por URI específico em vez de IP (útil para proteger endpoints específicos):
8. WebSocket Proxying
O suporte a WebSocket no Nginx requires a configuração específica para o upgrade de protocolo HTTP para WebSocket (nginx.org — WebSocket Proxying):
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location /ws/ {
proxy_pass http://127.0.0.1:8080;
# Upgrade HTTP para WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
# Headers standard
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_set_header X-Forwarded-Proto $scheme;
# Timeouts prolongados para ligações WebSocket longas
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Buffer desactivado para streaming em tempo real
proxy_buffering off;
}
}
O proxy_http_version 1.1 é obrigatório porque o WebSocket usa HTTP/1.1 com Upgrade. O proxy_read_timeout 3600s previne que o Nginx feche ligações WebSocket inactivas após 60 segundos (o defeito). Sem este timeout prolongado, ligações inactivas são terminadas, o que quebra aplicações de chat e dashboards em tempo real.
Para distinguir tráfego WebSocket de tráfego HTTP normal no mesmo servidor, usar map:
map $http_upgrade $connection_upgrade {
default upgrade;
” close;
}
server {
location / {
proxy_pass http://backend_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}
Esta abordagem com map envia Connection: upgrade apenas quando há um header Upgrade, e Connection: close para pedidos HTTP normais, evitando interferência.
9. Tuning de Performance: Worker Processes, Connections e Keepalive
A configuração de performance do Nginx vive no ficheiro principal nginx.conf (nginx.org — Core Functionality):
user nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Log format com upstream info
log_format main ‘$remote_addr – $remote_user [$time_local] ‘
‘”$request” $status $body_bytes_sent ‘
‘”$http_referer” “$http_user_agent” ‘
‘rt=$request_time uct=”$upstream_connect_time” ‘
‘urt=”$upstream_response_time”‘;
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65s;
keepalive_requests 1000;
types_hash_max_size 2048;
server_tokens off;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 256;
gzip_proxied any;
gzip_comp_level 5;
gzip_types text/plain text/css text/xml application/json
application/javascript application/xml
application/xml+rss image/svg+xml;
# Keepalive para upstreams — reutilizar ligações TCP
upstream backend_app {
server 10.0.0.11:8080;
server 10.0.0.12:8080;
keepalive 32;
}
include /etc/nginx/conf.d/*.conf;
}
Parâmetros críticos e sua ajustagem:
| Parâmetro | Default | Recomendado (PME) | Descrição |
|---|---|---|---|
worker_processes |
1 | auto |
Um worker por núcleo de CPU |
worker_connections |
512 | 4096 | Ligações por worker (cliente + upstream) |
keepalive_timeout |
75s | 65s | Tempo que a ligação cliente fica aberta |
keepalive_requests |
100 | 1000 | Pedidos por ligação keepalive |
worker_rlimit_nofile |
auto | 65535 | Limite de descritores de ficheiros |
multi_accept |
off | on | Aceitar todas as ligações na fila de uma vez |
sendfile |
off | on | Transferência zero-copy do kernel |
tcp_nopush |
off | on | enviar headers + início de ficheiro num pacote |
O keepalive 32 no bloco upstream mantém um pool de 32 ligações TCP persistentes para os backends, eliminando o custo de estabelecer nova ligação TCP a cada pedido. Isto requer que o backend suporte keepalive (Node.js, uWSGI, Gunicorn com --keep-alive).
Ajuste do limite de ficheiros do sistema (ulimit):
ulimit -n
# Ajustar permanentemente
echo “nginx soft nofile 65535” | sudo tee -a /etc/security/limits.conf
echo “nginx hard nofile 65535” | sudo tee -a /etc/security/limits.conf
# Para systemd (RHEL/Ubuntu modernos)
sudo systemctl edit nginx
# Adicionar:
[Service]
LimitNOFILE=65535
A fórmula para capacidade máxima de clientes simultâneos é: worker_processes × worker_connections. Com auto (8 núcleos) e 4096 ligações, o Nginx suporta 32.768 ligações simultâneas, o que é adequado para a maioria das PME. Cada ligação consome aproximadamente 2 descritores de ficheiros (cliente + upstream), daí o worker_rlimit_nofile ser 65535.
10. Troubleshooting: Logs, Status Codes e Diagnóstico
Logs de erro e de acesso:
O Nginx produz dois logs principais. O error.log regista problemas internos (falhas de ligação, timeouts, erros de configuração) e o access.log regista todos os pedidos processados (nginx.org — Logging).
sudo tail -50 /var/log/nginx/error.log
# Ver acessos com códigos 5xx
sudo awk ‘$9 ~ /^5/ {print}’ /var/log/nginx/access.log | tail -20
# Top IPs com mais pedidos
sudo awk ‘{print $1}’ /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# Análise de códigos de status
sudo awk ‘{print $9}’ /var/log/nginx/access.log | sort | uniq -c | sort -rn
Tabela de códigos de erro comuns e causas:
| Código | Significado | Causa Provável | Acção |
|---|---|---|---|
| 502 Bad Gateway | Backend não responde | Backend em baixo, porta errada, firewall | Verificar processo backend e porta |
| 503 Service Unavailable | Limite de rate limiting atingido | limit_req activo |
Ajustar rate ou burst |
| 504 Gateway Timeout | Backend demorou demasiado | proxy_read_timeout baixo, backend lento |
Aumentar timeout ou optimizar backend |
| 400 Bad Request | Pedido malformado | Header demasiado grande | Aumentar large_client_header_buffers |
| 413 Request Entity Too Large | Body do pedido excede limite | client_max_body_size baixo (default 1MB) |
Aumentar client_max_body_size |
| 429 Too Many Requests | Rate limiting | limit_req |
Esperar ou ajustar limites |
Diagnosticar problemas com curl:
curl -v http://127.0.0.1:8080/
# Testar via Nginx com headers detalhados
curl -vI https://app.example.com/
# Verificar certificado SSL
openssl s_client -connect app.example.com:443 -servername app.example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates -issuer -subject
# Testar WebSocket
curl -i -N \
-H “Connection: Upgrade” \
-H “Upgrade: websocket” \
-H “Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==” \
-H “Sec-WebSocket-Version: 13” \
https://app.example.com/ws/
Validar configuração antes de aplicar:
sudo nginx -t
# Aplicar sem reiniciar (reload gracioso)
sudo nginx -s reload
# Reabrir logs (após log rotation)
sudo nginx -s reopen
# Parar
sudo nginx -s stop
O comando nginx -t é essencial antes de qualquer reload. Um erro de sintaxe num ficheiro de configuração que faça o Nginx reiniciar causa interrupção de serviço. O nginx -s reload é gracioso: os workers antigos terminam as ligações em curso antes de encerrar.
11. Comparação com Traefik e Caddy
O Nginx não é a única opção. Traefik e Caddy são alternativas modernas com filosofias distintas:
| Característica | Nginx (Open Source) | Traefik | Caddy |
|---|---|---|---|
| Configuração | Ficheiros estáticos (conf) | Dinâmica (labels Docker, K8s CRDs) | Caddyfile (simples) |
| SSL automático | Via Certbot (manual) | Let’s Encrypt integrado | Let’s Encrypt integrado |
| Service Discovery | Manual (upstream blocks) | Automático (Docker, K8s, Consul) | Manual ou plugins |
| Health Checks | Limitados (passivos) | Activos integrados | Básicos |
| WebSocket | Configuração manual | Automático | Automático |
| Performance | Muito alta (C, event-driven) | Alta (Go) | Alta (Go) |
| Métricas | Módulo stub_status ou third-party | Prometheus integrado | Métricas básicas |
| API dinâmica | Nginx Plus (pago) | API REST integrada | API REST básica |
| Curva de aprendizado | Moderada a alta | Moderada (se usa Docker) | Baixa |
| Melhor para | Alto tráfego, controlo fino | Microserviços com Docker/K8s | Setup rápido, simplicidade |
Quando escolher cada um:
- Nginx: PME com infraestrutura tradicional (VMs, bare metal), alto volume de tráfego, necessidade de caching avançado e controlo granular sobre cada parâmetro.
- Traefik: PME que opera em Docker ou Kubernetes, onde os backends mudam frequentemente (deployments automáticos, scaling dinâmico). A configuração automática via labels elimina a necessidade de reescrever ficheiros de configuração (traefik.io).
- Caddy: PME que precisa de SSL automático sem Certbot e prefere configuração minimalista. O Caddy obter certificados Let’s Encrypt automaticamente por defeito é uma vantagem significativa (caddyserver.com).
Para uma PME que já tem Nginx em produção, a migração para Traefik ou Caddy só se justifica se a infraestrutura tiver mudado significativamente (adoção de Docker/K8s) ou se a gestão de certificados for uma carga operacional excessiva.
Checklist Pré-Produção
- [ ] Nginx instalado a partir do repositório oficial (versão estável recente)
- [ ]
nginx -tpassa sem erros antes de qualquer reload - [ ] Certificado SSL/TLS configurado via Let’s Encrypt com renovação automática testada (
certbot renew --dry-run) - [ ] Apenas TLS 1.2 e TLS 1.3 activos (
ssl_protocols TLSv1.2 TLSv1.3) - [ ] Redirect HTTP → HTTPS configurado (
return 301 https://$host$request_uri) - [ ] Headers
Host,X-Real-IP,X-Forwarded-For,X-Forwarded-Protoenviados ao backend - [ ] Health checks passivos configurados (
max_fails,fail_timeout,proxy_next_upstream) - [ ] Cache de conteúdo estático activo com
proxy_cache_pathe permissões correctas - [ ] Headers de segurança aplicados com
always(HSTS, X-Frame-Options, CSP, X-Content-Type-Options) - [ ] Rate limiting configurado em endpoints sensíveis (login, API) com
limit_req_status 429 - [ ] WebSocket proxying configurado com
proxy_http_version 1.1,Upgrade,Connectione timeouts prolongados - [ ]
worker_processes autoeworker_connectionsajustados ao hardware - [ ]
keepalive_timeoutekeepalive_requestsconfigurados no contextohttp - [ ]
keepalive 32(ou superior) no upstream para reutilização de ligações TCP - [ ]
server_tokens offpara ocultar versão do Nginx - [ ]
client_max_body_sizeajustado conforme a aplicação (default 1MB pode ser insuficiente) - [ ] Gzip activo com
gzip_typesadequados - [ ] Log format com
request_timeeupstream_response_timepara diagnóstico - [ ] Limites de ficheiros do sistema (
ulimit -nou systemdLimitNOFILE) ajustados - [ ] Log rotation configurado para
access.logeerror.log - [ ] Firewall configurado: apenas portas 80 e 443 expostas; backend acessível só via localhost ou rede interna
- [ ] Backups da configuração em controlo de versões (git)
