Nginx — Coaching app e documentação
Utiliza estes exemplos como base no mesmo servidor da app (Next.js + pm2) e do site MkDocs em docs.diasantos.com.
Ficheiros no repositório
Os mesmos conteúdos estão em documentation/examples/nginx/ para copiares para /etc/nginx/sites-available/ sem passar pelo browser.
Activar um site no nginx (Debian / Ubuntu)
Fluxo típico para cada domínio (substitui docs.diasantos.com pelo nome do ficheiro que quiseres; para a app usa por exemplo coaching.app ou o domínio real).
1. Instalar o nginx (se ainda não existir)
sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx
2. Criar o ficheiro do virtual host
Opção A — copiar o exemplo do repositório (ajusta o caminho do clone):
sudo cp /var/www/coaching/documentation/examples/nginx/docs.diasantos.com.conf.example \
/etc/nginx/sites-available/docs.diasantos.com
Opção B — criar/editar à mão com o editor que preferires:
sudo vim /etc/nginx/sites-available/docs.diasantos.com
# ou: sudo nano /etc/nginx/sites-available/docs.diasantos.com
Cola o conteúdo do exemplo (secções abaixo ou ficheiro .example), grava e sai (:wq no vim, Ctrl+O / Ctrl+X no nano).
Antes do primeiro HTTPS
Se ainda não tens certificados, podes começar só com o bloco listen 80 e server_name, servir o site em HTTP, emitir o certificado com certbot, e só depois acrescentar o bloco listen 443 ssl ou deixar o certbot alterar o ficheiro por ti. Os exemplos assumem que os paths do Let's Encrypt já existem.
3. Activar o site (link simbólico)
O nginx só carrega o que estiver em sites-enabled/. Cria um link para o ficheiro em sites-available/:
sudo ln -sf /etc/nginx/sites-available/docs.diasantos.com \
/etc/nginx/sites-enabled/docs.diasantos.com
-fsubstitui o link se já existir (útil ao repetir o comando).- Repete o mesmo padrão para a app, por exemplo:
/etc/nginx/sites-available/coaching.app→sites-enabled/coaching.app.
4. Conflito com o site por defeito (opcional)
Em instalações novas, /etc/nginx/sites-enabled/default pode ocupar a porta 80. Se precisares dessa porta para o teu server_name, desactiva o default:
sudo rm /etc/nginx/sites-enabled/default
# ou: sudo unlink /etc/nginx/sites-enabled/default
5. Validar e aplicar
sudo nginx -t
Se aparecer syntax is ok e test is successful:
sudo systemctl reload nginx
Confirma no browser (https://docs.diasantos.com) ou com curl -I https://docs.diasantos.com.
6. Ver erros se algo falhar
sudo journalctl -u nginx -n 50 --no-pager
Coaching (reverse proxy para Next.js)
Pressupõe next start ou equivalente em 127.0.0.1:3000 (porta típica do pm2). Ajusta server_name, caminhos dos certificados e a proxy_pass se a porta for outra.
# Exemplo: reverse proxy HTTPS para a app Next.js (pm2 em 127.0.0.1:3000).
#
# Uso no servidor Debian/Ubuntu:
# sudo cp coaching.app.conf.example /etc/nginx/sites-available/coaching.app
# sudo ln -sf /etc/nginx/sites-available/coaching.app /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginx
#
# Substitui app.diasantos.com pelo teu domínio da aplicação.
# Certificados: certbot certonly --nginx -d app.diasantos.com
server {
listen 80;
listen [::]:80;
server_name app.diasantos.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.diasantos.com;
ssl_certificate /etc/letsencrypt/live/app.diasantos.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.diasantos.com/privkey.pem;
# Opcional: endurecer TLS (ajusta à tua política)
# ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 25m;
# POST /api/admin/deploy — SSE de longa duração (`next build` pode passar de 5–10 min com poucos bytes).
# Um `proxy_read_timeout` curto fecha a ligação ao upstream → no browser **net::ERR_INCOMPLETE_CHUNKED_ENCODING**.
location = /api/admin/deploy {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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_buffering off;
proxy_cache off;
gzip off;
proxy_read_timeout 7200s;
proxy_send_timeout 7200s;
}
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;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 300;
}
}
A app também envia comentários SSE periódicos durante os passos do deploy para o proxy ver leituras regulares; convém manter proxy_read_timeout alto neste caminho no nginx.
Documentação (MkDocs estático em docs.diasantos.com)
Depois de mkdocs build, o conteúdo fica em documentation/site/. O root abaixo deve apontar para essa pasta no servidor.
# Exemplo: site estático MkDocs (saída de: cd documentation && mkdocs build).
#
# Uso no servidor Debian/Ubuntu:
# sudo cp docs.diasantos.com.conf.example /etc/nginx/sites-available/docs.diasantos.com
# sudo ln -sf /etc/nginx/sites-available/docs.diasantos.com /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginx
#
# Ajusta root ao caminho real do clone (APP_DIR/documentation/site).
# Certificados: certbot certonly --nginx -d docs.diasantos.com
server {
listen 80;
listen [::]:80;
server_name docs.diasantos.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name docs.diasantos.com;
ssl_certificate /etc/letsencrypt/live/docs.diasantos.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docs.diasantos.com/privkey.pem;
root /var/www/coaching/documentation/site;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Opcional: cache curto para assets estáticos gerados pelo MkDocs
location ~* \.(?:css|js|woff2?|png|jpg|jpeg|gif|svg|ico)$ {
try_files $uri =404;
expires 7d;
add_header Cache-Control "public, max-age=604800, immutable";
}
}
Certificados com Certbot (Let's Encrypt)
Usa o plugin nginx do Certbot para emitir e renovar certificados sem parar o servidor de forma desnecessária (desafio via nginx).
Pré-requisitos
- DNS: registos A (e opcionalmente AAAA) para
app.diasantos.comedocs.diasantos.coma apontar para o IP público deste servidor (substitui pelos teus domínios reais). - Porta 80 acessível de Internet (Let's Encrypt usa HTTP-01 por defeito com
--nginx). - Nginx a correr com pelo menos o bloco HTTP (porta 80) e
server_namecorrecto antes de pedires HTTPS — se o ficheiro tiverssl_certificatepara ficheiros que ainda não existem, onginx -tfalha; nesse caso começa só comlisten 80(como no aviso da secção de activação), obtém o certificado, e só depois acrescenta ou descomenta o bloco 443 dos exemplos.
Instalar o Certbot
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
Emitir um certificado por site (recomendado)
Com os dois virtual hosts activos em sites-enabled e HTTP a responder para cada server_name:
# App (reverse proxy Next.js)
sudo certbot --nginx -d app.diasantos.com
# Documentação (MkDocs estático)
sudo certbot --nginx -d docs.diasantos.com
O Certbot pergunta email, aceitação dos termos e se queres redireccionar HTTP→HTTPS (equivalente aos return 301 dos exemplos). No fim, costuma ajustar os teus server { ... } para usar fullchain.pem e privkey.pem em /etc/letsencrypt/live/<domínio>/.
Alinhar com os exemplos desta página
Se já tinhas colado os blocos completos dos exemplos (proxy, root, cabeçalhos), faz backup antes (sudo cp …/sites-available/… /tmp/) e, depois do Certbot, verifica com sudo nginx -t. Se o Certbot simplificar demasiado um bloco (por exemplo perder proxy_set_header na app), recupera essas linhas do backup e mantém os ssl_certificate que o Certbot configurou.
Um único certificado para os dois nomes (opcional)
Útil se quiseres um só certificado SAN para ambos os hosts:
sudo certbot --nginx -d app.diasantos.com -d docs.diasantos.com
Nesse caso ambos os server { … } em HTTPS devem referenciar o mesmo par fullchain.pem / privkey.pem (normalmente o primeiro nome da lista define o directório em /etc/letsencrypt/live/). Os exemplos separados por domínio assumem um certificado por site (um server_name por certificado).
Verificar e recarregar
sudo nginx -t && sudo systemctl reload nginx
sudo certbot certificates
Renovação automática
O pacote instala normalmente um timer systemd que corre certbot renew:
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
Em Debian/Ubuntu, após renovação bem-sucedida o hook costuma recarregar o nginx; se alterares configuração manualmente, confirma que renew continua a funcionar com o dry-run.
Alternativa: só obter o certificado (sem o plugin alterar o nginx)
Se preferires emitir o certificado e tu editares o nginx à mão:
sudo certbot certonly --nginx -d app.diasantos.com
sudo certbot certonly --nginx -d docs.diasantos.com
Depois define ssl_certificate / ssl_certificate_key como nos exemplos abaixo (paths sob /etc/letsencrypt/live/<domínio>/).
Teste rápido depois de tudo
curl -fsSI https://app.diasantos.com | head -n 5
curl -fsSI https://docs.diasantos.com | head -n 5
Se não usares /var/www/certbot no bloco /.well-known/acme-challenge/ e usares webroot manualmente, alinha root com o webroot que passares ao Certbot.
Ver também
- Alojar docs.diasantos.com — build do MkDocs e caminhos no disco.