Instalar Certbot no nginx do Ubuntu 24.04
Veja como instalar o Certbot via apt ou snap, emitir o certificado com certbot --nginx e evitar o erro de renovação por timeout na porta 80.
Instalar o Certbot: apt ou snap
No Ubuntu 24.04, sudo apt install certbot python3-certbot-nginx instala um Certbot funcional que emite certificados Let's Encrypt reais e reconhecidos publicamente. A documentação oficial do Certbot recomenda o snap; a diferença é pequena: o snap acompanha as versões upstream, enquanto o pacote do repositório acompanha a versão incluída no LTS e recebe atualizações de segurança.
Escolha uma opção. Duas cópias do Certbot significam dois temporizadores de renovação a atuar sobre a mesma árvore /etc/letsencrypt. A cópia que foi esquecida é a que pode causar surpresas.
O método com apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxIsto instala /usr/bin/certbot, o plugin do nginx, um par certbot.service + certbot.timer e uma entrada /etc/cron.d/certbot que não executa nenhuma ação sob systemd.
O método com snap:
sudo apt remove certbot python3-certbot-nginx
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotO snap instala o seu próprio temporizador, snap.certbot.renew.timer. Remova o pacote apt antes de instalar o snap.
Depois disso, ambas as instalações funcionam da mesma forma. Por predefinição, o Certbot 2.x utiliza chaves ECDSA (P-256). Use --key-type rsa apenas com um cliente que não suporte ECDSA. Todo o estado fica em /etc/letsencrypt: archive/ contém os ficheiros reais das chaves e dos certificados, live/ aponta por symlink para os ficheiros atuais, renewal/ contém um ficheiro de configuração por certificado e accounts/ contém a chave da sua conta ACME.
O que o HTTP-01 faz realmente e por que a porta 80 não é opcional
O desafio HTTP-01 é um callback. Você pede à Let's Encrypt um certificado que cubra example.com; o serviço resolve o nome no DNS público, abre uma ligação à porta 80 no endereço encontrado e solicita http://example.com/.well-known/acme-challenge/<token>. O seu servidor responde com o conteúdo exato do token que o Certbot acabou de gravar no disco. Esse é todo o mecanismo. Daqui resultam três consequências, que explicam a maioria das emissões que falham.
- A porta 80 tem de estar acessível a partir da Internet pública, não apenas a partir do seu portátil. Uma regra
ufw, um grupo de segurança do fornecedor de cloud ou uma firewall da consola da VPS que abra apenas a porta 443 interrompe a emissão e todas as renovações futuras. - O DNS já tem de apontar para este servidor. O servidor de validação faz a sua própria resolução a partir do exterior; as suas entradas
/etc/hostse a cache do navegador não têm qualquer efeito sobre ele. - Se publicar um registo AAAA, o IPv6 será tentado primeiro. A Let's Encrypt tenta novamente por IPv4 quando a ligação IPv6 falha de imediato, mas um registo AAAA desatualizado que aponte para um host que aceita a ligação e serve outro conteúdo provoca uma falha definitiva.
Os redirecionamentos são permitidos: a validação segue um redirecionamento HTTP para HTTPS e não considera relevante que o certificado do outro lado esteja ausente, expirado ou seja autoassinado. O que não fará é começar noutro local que não a porta 80. O Certbot não tem uma implementação de TLS-ALPN-01, portanto “usar apenas a porta 443” não é uma alternativa.
Escolher um autenticador: --nginx, --webroot, --standalone
--nginx é a opção padrão correta quando o nginx já está em execução e já serve o domínio. O Certbot analisa a sua configuração, injeta temporariamente uma localização para o desafio, recarrega o nginx, valida o domínio e depois escreve as diretivas TLS no seu bloco de servidor. Não há indisponibilidade.
sudo certbot --nginx -d example.com -d www.example.comPara execução por script, num servidor novo:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot é adequado quando não quer que o Certbot altere a configuração do nginx, por exemplo, quando esta é gerada a partir de um modelo, mantida no git ou aplicada com Ansible. O Certbot escreve apenas o ficheiro do desafio num diretório que já é servido pelo nginx.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone é adequado quando não há nenhum serviço a escutar na porta 80: por exemplo, um servidor de correio, uma API que só aceita ligações na porta 443 ou um script de primeiro arranque executado antes de o nginx existir. O Certbot liga-se à porta 80 durante alguns segundos. Se o nginx estiver em execução, este método falha. Pare-o antes da execução e inicie-o depois:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Esses hooks ficam registados na configuração de renovação do certificado. Assim, a mesma paragem e o mesmo arranque ocorrem automaticamente durante a renovação.
Um bloco de servidor que funciona antes e depois de o certificado existir
O problema do ovo e da galinha: o nginx recusa arrancar com ssl_certificate a apontar para um ficheiro que não existe, e o Certbot não consegue validar enquanto o nginx está parado. Coloque primeiro o site a funcionar na porta 80.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
}Execute sudo nginx -t && sudo systemctl reload nginx, confirme que curl -I http://example.com/ responde a partir de fora do servidor e, em seguida, emita o certificado. Depois:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}O prefixo ^~ na localização ACME é essencial: impede que o bloco return 301 intercepte o pedido de desafio. Manter essa localização na porta 80 permite que as renovações continuem a funcionar depois de o restante site aceitar apenas HTTPS.
Ambos os blocos acima servem ficheiros do disco. Se o nginx estiver, em vez disso, à frente de uma aplicação, location / passa a ser um bloco proxy_pass e o bloco de servidor do reverse proxy, linha a linha descreve os cabeçalhos de que essa aplicação precisa, enquanto a localização ACME e as diretivas TLS permanecem exatamente iguais.
A sintaxe do HTTP/2 depende da versão do nginx. Misturar as duas formas provoca um erro no arranque. O Ubuntu 24.04 inclui o nginx 1.24, que exige a forma na mesma linha, listen 443 ssl http2;. O Debian 13 inclui uma versão mais recente do nginx, que exige a diretiva http2 on; separada. Verifique primeiro nginx -v.
Aponte o nginx para live/, nunca para archive/. Os links simbólicos live/ são redirecionados em cada renovação. Um caminho fixo dentro de archive/ deixa o servidor preso a um certificado que expira sem ser substituído.
Wildcards exigem DNS-01, e DNS-01 exige um plugin
Um certificado wildcard (*.example.com) não pode ser validado através de HTTP-01, porque não existe um único hostname de onde obter um ficheiro. DNS-01 é o único caminho: prova que controla o domínio ao publicar um registo TXT _acme-challenge.example.com. O Certbot precisa de credenciais de API do seu fornecedor de DNS para fazer isso sem intervenção, e é para esse fim que existem os plugins dos fornecedores. O guia completo para certificados wildcard explica o funcionamento dos registos TXT e o problema da renovação no modo manual; segue uma versão curta para Cloudflare.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareNo caminho com apt, use sudo apt install python3-certbot-dns-cloudflare. Guarde as credenciais num ficheiro acessível apenas por root:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereLimite o token a permissões para editar o DNS apenas nessa zona. É uma chave para o seu DNS; trate-a como tal.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'Coloque o wildcard entre aspas para impedir que a shell faça a expansão de padrões. DNS-01 também resolve o que HTTP-01 não consegue: certificados para hosts sem a porta pública 80, um serviço interno, uma máquina acessível apenas através de uma VPN WireGuard auto-hospedada num VPS ou um painel de administração numa interface privada.
Renovação: os 90 dias, o timer, o deploy hook
Os certificados Let's Encrypt são válidos por 90 dias. O Certbot renova-os quando faltam menos de 30 dias para expirarem. Isto dá-lhe uma janela de 30 dias em que uma renovação com falha ainda é um problema corrigível, e não uma indisponibilidade. O Let's Encrypt já não envia mensagens de aviso de expiração. Ninguém o vai alertar, por isso a monitorização é agora da sua responsabilidade.
Verifique o timer instalado pelo seu sistema:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew percorre todas as configurações em /etc/letsencrypt/renewal/, ignora as que estão fora da janela de 30 dias e renova as restantes usando exatamente as flags da execução original. Por isso, a primeira execução é importante: é nessa execução que as opções ficam registadas.
Renovar o ficheiro no disco não altera nada por si só. O nginx continua a servir o certificado antigo que mantém em memória até receber uma instrução para recarregar a configuração. Configure um deploy hook uma vez:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shQualquer ficheiro executável em renewal-hooks/deploy/ é executado depois de qualquer renovação bem-sucedida. A flag --deploy-hook faz o mesmo para um certificado específico e guarda renew_hook = ... na respetiva configuração de renovação. certbot --nginx faz o reload por si; as configurações --webroot e --standalone não o fazem. A ausência de um hook é precisamente o motivo pelo qual um site pode servir um certificado expirado enquanto certbot certificates indica corretamente que existe um certificado novo. Qualquer outro serviço que leia o certificado durante o arranque precisa do mesmo hook. Uma aplicação contentorizada, como uma instalação do Nextcloud VPS com Docker, TLS e backups, também precisa de ter aqui configurado o seu próprio passo de reinício ou reload.
Testar a renovação de forma real
sudo certbot renew --dry-runIsto executa o desafio completo no ambiente de staging da Let's Encrypt: o mesmo caminho de código, a mesma firewall, o mesmo DNS, sem consumir limites de frequência e sem escrever nada no disco. Se passar hoje, a renovação automática daqui a 60 dias também passará, desde que nada mude entretanto no servidor.
Um teste simulado não prova que o hook de reload é executado. O comportamento varia consoante a versão do Certbot. Teste essa parte manualmente: execute diretamente o script do hook, confirme que systemctl reload nginx é concluído com sucesso e verifique sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
Os erros que você encontrará na prática
Could not bind to IPv4 or IPv6., --standalone enquanto o nginx já ocupa a porta 80. Use --nginx ou --webroot, ou pare o nginx durante a execução. Confirme qual processo ocupa a porta com sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem), o Let's Encrypt não conseguiu aceder à porta 80. Verifique de dentro para fora: sudo ufw status (abra-a com sudo ufw allow 'Nginx Full'), depois a firewall do próprio provedor do VPS e, por fim, o DNS. Teste a partir de um local que não seja o seu servidor: curl -sSv http://example.com/.well-known/acme-challenge/test. Um registo AAAA obsoleto produz esta mesma mensagem.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404, a porta 80 está acessível, mas o token não está a ser servido. O pedido chegou a outro bloco de servidor (verifique qual deles controla default_server), ou o diretório passado a -w não é o diretório servido pelo nginx. Crie um ficheiro em /var/www/example.com/.well-known/acme-challenge/test e tente obtê-lo externamente; se o resultado for 404, o certificado nunca foi o problema.
DNS problem: NXDOMAIN looking up A for example.com, o nome não é resolvido publicamente. Pode tratar-se de registos novos que ainda não foram propagados ou de um registo numa zona que o seu registrador não está a servir.
too many certificates already issued for: example.com, um limite de taxa, que é o limite atingido durante a depuração repetida. O Let's Encrypt limita os certificados duplicados, com exatamente o mesmo conjunto de nomes, a cinco por semana. Separadamente, permite 50 certificados novos por domínio registado por semana. Nada desbloqueia nenhum dos limites além do tempo. Faça a depuração no ambiente de staging com --dry-run.
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory, o nginx está configurado para um certificado que nunca foi emitido ou que foi removido com certbot delete. Comente o bloco de servidor TLS, inicie o nginx, emita o certificado e restaure o bloco.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed, esse ficheiro é incluído no pacote do plugin do nginx. Numa máquina certonly sem python3-certbot-nginx, instale o plugin ou substitua a linha include pelas suas próprias definições ssl_protocols e ssl_ciphers.
Gerir isto em escala
Um certificado pode conter até 100 nomes, e um único certbot --nginx -d a.example.com -d b.example.com ... parece tentador, até que um registo DNS obsoleto falhe a validação e faça todos os outros nomes desse certificado falharem também. Certificados separados por site falham de forma independente, que é o comportamento pretendido num servidor que aloja mais do que alguns serviços. Quando ultrapassa um pequeno número de sites, uma porta de entrada com suporte para ACME compensa: um reverse proxy Traefik que executa várias aplicações com Docker Compose solicita e renova os certificados por si próprio, e o Certbot deixa de ser necessário. A escolha do proxy para essa porta de entrada é uma decisão à parte, e comparar Nginx com Caddy e Traefik depende sobretudo de quanto trabalho de certificados e de configuração por aplicação pretende delegar no proxy.
Faça uma cópia de segurança completa de /etc/letsencrypt, incluindo sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt, com os links simbólicos intactos. Essa árvore contém accounts/, a chave da conta ACME, que não pode regenerar de forma idêntica. A migração para um novo VPS resume-se então a: sincronizar a árvore com -a, instalar o Certbot, apontar o DNS para o novo servidor e executar certbot renew --dry-run antes de fazer a mudança.
Se reconstruir o servidor ou mudar para uma nova versão LTS, o temporizador de renovação não será transferido. Depois de qualquer migração, restauração de um snapshot ou atualização da distribuição, execute systemctl list-timers 'certbot*' e um --dry-run. Ignorar este passo pode fazer com que um site fique indisponível 89 dias depois, às 3am, com um certificado que todos presumiam estar a ser renovado automaticamente.
Tudo isto pressupõe uma máquina que controla, com um IP público e a porta 80 aberta para a Internet: um VPS, por outras palavras. Os procedimentos acima são idênticos em qualquer uma dessas máquinas.
Os mesmos passos para certificados aplicam-se no Apache em vez do nginx e, quando não é possível utilizar um certificado público, um certificado autoassinado no Ubuntu permite proteger serviços internos.
FAQ
Preciso de ter a porta 80 aberta se o meu site só disponibiliza HTTPS?
Sim, para o desafio HTTP-01. O Let's Encrypt inicia sempre o pedido de validação na porta 80, e o Certbot não tem uma implementação de TLS-ALPN-01. Por isso, uma firewall que abre apenas a porta 443 bloqueia tanto a primeira emissão como todas as renovações automáticas seguintes. Um redirecionamento da porta 80 para HTTPS funciona; a validação segue-o. A única forma de omitir completamente a porta 80 é usar DNS-01 com um plugin do fornecedor.
apt ou snap: qual Certbot devo instalar para o nginx no Ubuntu 24.04?
Use apt. sudo apt install certbot python3-certbot-nginx disponibiliza o Certbot 2.9.0 no Ubuntu 24.04. Esta versão é suficientemente atual para tudo o que é apresentado neste guia, recebe correções de segurança através de unattended-upgrades e não requer snapd. Escolha snap apenas se precisar imediatamente da versão mais recente ou de um plugin DNS distribuído exclusivamente como snap. Em qualquer caso, escolha exatamente um: duas instalações significam dois temporizadores de renovação apontados para a mesma árvore /etc/letsencrypt, e a instalação esquecida é a que causa problemas.
O Certbot pode emitir um certificado wildcard para o nginx?
Apenas através de DNS-01. Um wildcard como *.example.com não tem um único nome de anfitrião a partir do qual seja possível obter um ficheiro de desafio. Por isso, --nginx, --webroot e --standalone estão todos excluídos. Instale o plugin do seu fornecedor DNS, coloque um token de API com permissões limitadas num ficheiro de credenciais acessível apenas por root e execute certbot certonly --dns-cloudflare -d example.com -d '*.example.com', colocando o wildcard entre aspas para impedir que o shell o interprete como um padrão glob.
Por que motivo o nginx continua a disponibilizar o certificado antigo depois de uma renovação bem-sucedida?
O nginx mantém o certificado em memória e não deteta o novo ficheiro no disco até recarregar a configuração. certbot --nginx faz o recarregamento automaticamente, mas --webroot e --standalone não o fazem. Por isso, uma renovação pode ser concluída com sucesso enquanto o navegador continua a ver um certificado prestes a expirar. Coloque um script executável em /etc/letsencrypt/renewal-hooks/deploy/ que execute nginx -t && systemctl reload nginx. O script será executado depois de cada renovação bem-sucedida.
certbot renew --dry-run garante que a renovação vai funcionar?
Na maioria dos casos. O comando executa o desafio real no ambiente de staging, usando a mesma firewall, o mesmo DNS e o mesmo caminho de código, sem consumir limites de frequência nem escrever dados no disco. Por isso, um resultado bem-sucedido confirma que a parte de rede está correta. No entanto, não confirma de forma fiável que o deploy hook é executado. Teste-o separadamente: execute o script do hook manualmente e verifique sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.