Como instalar Certbot para nginx no Ubuntu 24.04
Aprenda a instalar Certbot via apt ou snap no Ubuntu 24.04. Evite conflitos de timers de renovação e saiba como configurar o plugin nginx para Let's Encrypt.
Instalar Certbot: apt ou snap
No Ubuntu 24.04, o sudo apt install certbot python3-certbot-nginx fornece um Certbot funcional que emite certificados reais e confiáveis da Let's Encrypt. A documentação oficial do Certbot recomenda o uso do snap; a diferença é pequena — o snap acompanha os lançamentos oficiais, enquanto o pacote do repositório acompanha a versão enviada com o LTS e recebe correções de segurança.
Escolha um. Ter duas cópias do Certbot significa dois timers de renovação apontando para a mesma árvore /etc/letsencrypt, e a versão que você esquecer será a que causará problemas.
O caminho via apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxIsso instala o /usr/bin/certbot, o plugin nginx, um par certbot.service + certbot.timer e uma entrada /etc/cron.d/certbot que não executa nada sob o systemd.
O caminho via 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 possui seu próprio timer, o snap.certbot.renew.timer. Remova o pacote apt antes de instalar o snap.
Ambas as instalações se comportam da mesma forma depois de prontas. O Certbot 2.x usa ECDSA (P-256) por padrão — use --key-type rsa apenas para clientes que não suportam ECDSA. Todo o estado é armazenado em /etc/letsencrypt: archive/ contém os arquivos reais de chave e certificado, live/ contém links simbólicos para os atuais, renewal/ contém um arquivo de configuração por certificado e accounts/ contém sua chave de conta ACME.
O que o HTTP-01 realmente faz, e por que a porta 80 não é opcional
O desafio HTTP-01 é um callback. Você solicita ao Let's Encrypt um certificado para example.com; o serviço resolve o nome no DNS público, abre uma conexão na porta 80 no endereço encontrado e solicita http://example.com/.well-known/acme-challenge/<token>. Seu servidor responde com o conteúdo exato do token que o Certbot acabou de gravar no disco. Esse é todo o mecanismo. Três consequências derivam disso e explicam a maioria das falhas de emissão.
- A porta 80 deve estar acessível pela internet pública, não apenas pelo seu laptop. Uma regra de
ufw, um security group de provedor de nuvem ou um firewall de console de VPS que abra apenas a 443 interrompe a emissão e todas as renovações futuras. - O DNS já deve apontar para este servidor. O servidor de validação realiza sua própria consulta externa; suas entradas
/etc/hostse o cache do navegador não têm efeito para ele. - Se você publicar um registro AAAA, o IPv6 é testado primeiro. O Let's Encrypt tenta novamente via IPv4 quando a conexão IPv6 falha completamente — mas um AAAA desatualizado apontando para um host que aceita a conexão e serve outro conteúdo causa uma falha definitiva.
Redirecionamentos são permitidos: a validação segue um redirecionamento HTTP para HTTPS e não se importa se o certificado no destino está ausente, expirado ou é autoassinado. O que o processo não faz é iniciar por qualquer porta que não seja a 80. O Certbot não possui implementação de TLS-ALPN-01, portanto, "apenas use a 443" não é uma alternativa.
Escolhendo um authenticator: --nginx, --webroot, --standalone
--nginx é a opção padrão correta quando o nginx já está em execução e servindo o domínio. O Certbot analisa sua configuração, injeta um local temporário para o challenge, recarrega o nginx, valida e escreve as diretivas TLS no seu server block. Sem downtime.
sudo certbot --nginx -d example.com -d www.example.comPara automação em um 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 é a opção correta quando você não quer o Certbot alterando sua configuração do nginx — como no caso de configurações geradas por templates, mantidas no git ou enviadas via Ansible. O Certbot escreve apenas o arquivo de challenge em um diretório que você já serve.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone é a opção correta quando nada está escutando na porta 80: um servidor de e-mail, uma API que opera apenas na porta 443 ou um script de primeiro boot que executa antes do nginx existir. O Certbot vincula a porta 80 por alguns segundos. Se o nginx estiver em execução, isso falhará — pare o serviço antes de rodar:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Esses hooks são registrados na configuração de renovação do certificado, permitindo que o processo de stop/start ocorra automaticamente durante a renovação.
Um server block que funciona antes e depois do certificado existir
O problema do "ovo e a galinha": o nginx recusa a iniciar se o ssl_certificate apontar para um arquivo inexistente, e o Certbot não consegue validar o domínio enquanto o nginx estiver offline. Suba o site na porta 80 primeiro.
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 as respostas de curl -I http://example.com/ de fora do servidor e, então, 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 ^~ no location ACME é essencial: ele impede que o bloco return 301 capture a requisição de desafio. Manter esse location na porta 80 garante que as renovações continuem funcionando após o restante do site migrar para HTTPS.
A sintaxe do HTTP/2 depende da sua versão do nginx; misturar as duas formas causa erro de inicialização. O Ubuntu 24.04 utiliza o nginx 1.24, que exige a sintaxe inline — listen 443 ssl http2;. O Debian 13 utiliza um nginx mais recente, que exige a diretiva http2 on; separada. Verifique nginx -v primeiro.
Aponte o nginx para live/, nunca para archive/. Os symlinks de live/ são atualizados em cada renovação; usar um caminho absoluto para archive/ prende você a um certificado que irá expirar.
Wildcards significam DNS-01, e DNS-01 significa um plugin
Um certificado wildcard (*.example.com) não pode ser validado via HTTP-01 — não existe um hostname único para buscar o arquivo. O DNS-01 é o único caminho: você prova o controle publicando um registro TXT _acme-challenge.example.com. O Certbot precisa de credenciais de API do seu provedor de DNS para realizar o processo de forma automatizada; os plugins de provedor existem para isso. O passo a passo completo do certificado wildcard aborda a mecânica do registro TXT e o problema de renovação no modo manual; a versão curta para Cloudflare vem a seguir.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareNo caminho apt, o processo é sudo apt install python3-certbot-dns-cloudflare. As credenciais devem ficar em um arquivo acessível apenas pelo root:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereLimite o token apenas a permissões de edição de DNS para essa zona específica. Ele é uma chave para o seu DNS; trate-o 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 que o shell não o interprete como um padrão de busca (glob). O DNS-01 também resolve o que o HTTP-01 não consegue: certificados para hosts sem porta 80 pública — um serviço interno, um servidor acessível apenas via self-hosted WireGuard VPN on a VPS, ou um painel de administração em uma interface privada.
Renovação: os 90 dias, o timer e o deploy hook
Certificados Let's Encrypt são válidos por 90 dias. O Certbot renova quando restam menos de 30 dias. Isso oferece uma janela de 30 dias para corrigir falhas de renovação antes que ocorra uma interrupção do serviço. O Let's Encrypt não envia mais e-mails de aviso de expiração — o monitoramento agora é responsabilidade do administrador.
Verifique o timer da sua instalação:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatesO certbot renew analisa cada configuração em /etc/letsencrypt/renewal/, ignora o que estiver fora da janela de 30 dias e renova o restante usando exatamente as flags da execução original. Por isso a primeira execução é importante: é ela que fica registrada.
Renovar o arquivo no disco não altera o serviço sozinho — o nginx continua servindo o certificado antigo da memória até que receba um comando de reload. Configure um deploy hook uma única 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 executável em renewal-hooks/deploy/ é executado após qualquer renovação bem-sucedida. A flag --deploy-hook faz o mesmo para um único certificado, armazenando renew_hook = ... em sua configuração de renovação. O certbot --nginx faz o reload para você; as configurações --webroot e --standalone não. A ausência de um hook é o motivo exato de um site servir um certificado expirado enquanto o certbot certificates reporta um certificado novo. Qualquer outro processo que leia o certificado na inicialização precisa do mesmo hook — um app em container, como uma instalação Nextcloud VPS com Docker, TLS e backups, também precisa de um passo de restart ou reload configurado aqui.
Teste de renovação real
sudo certbot renew --dry-runIsso executa o desafio completo contra o ambiente de staging do Let's Encrypt: mesmo caminho de código, mesmo firewall, mesmo DNS, sem custo de rate-limit e sem escrita em disco. Se passar hoje, a renovação automática em 60 dias também passará, assumindo que nenhuma configuração do servidor mude.
Um dry run não prova que o seu reload hook funciona — o comportamento varia conforme a versão do Certbot. Teste essa parte manualmente: execute o script do hook diretamente, confirme se systemctl reload nginx tem sucesso e verifique sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
Os erros que você realmente encontrará
Could not bind to IPv4 or IPv6. — --standalone porque o nginx já está utilizando a porta 80. Use --nginx ou --webroot, ou pare o nginx antes de rodar. Confirme o processo que ocupa a porta com sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem) — O Let's Encrypt não conseguiu alcançar a porta 80. Verifique a progressão: sudo ufw status (abra a porta com sudo ufw allow 'Nginx Full'), depois o firewall do próprio provedor de VPS e, por fim, o DNS. Teste de um local que não seja o seu servidor: curl -sSv http://example.com/.well-known/acme-challenge/test. Um registro AAAA desatualizado 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á sendo servido. A requisição caiu em um server block diferente (verifique qual deles contém default_server), ou o diretório passado para -w não é o diretório servido pelo nginx. Coloque um arquivo em /var/www/example.com/.well-known/acme-challenge/test e tente acessá-lo externamente; se retornar 404, o certificado não é o problema.
**DNS problem: NXDOMAIN looking up A for example.com — o nome não resolve publicamente. Registros novos que ainda não propagaram, ou um registro em uma zona que o seu registrador não está servindo.
too many certificates already issued for: example.com — um limite de taxa (rate limit), comum ao depurar em loops. O Let's Encrypt limita certificados duplicados — o mesmo conjunto exato de nomes — a cinco por semana, e permite separadamente 50 novos certificados por domínio registrado por semana; nada desbloqueia ambos exceto o tempo. Depure usando o 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 um que foi removido com certbot delete. Comente o server block de TLS, inicie o nginx, emita o certificado e restaure o bloco.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed — esse arquivo é instalado com o pacote do plugin do nginx. Em uma máquina certonly sem python3-certbot-nginx, adicione o plugin ou substitua a linha include pelas suas próprias configurações de ssl_protocols e ssl_ciphers.
Gerenciando isso em escala
Um certificado pode conter até 100 nomes, e um único certbot --nginx -d a.example.com -d b.example.com ... é tentador — até que um registro DNS obsoleto falhe na validação e derrube todos os outros nomes desse certificado. Certificados separados por site falham de forma independente, que é o comportamento desejado em um servidor que hospeda mais de alguns serviços. Para mais de alguns sites, um proxy reverso compatível com ACME vale o investimento: um Traefik reverse proxy rodando múltiplos apps via Docker Compose solicita e renova os certificados sozinho, e o Certbot torna-se desnecessário.
Faça backup do /etc/letsencrypt completo — sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt — com os symlinks intactos. Esse diretório contém o accounts/, sua chave de conta ACME, que não pode ser regenerada de forma idêntica. Migrar para um novo VPS resume-se a: usar rsync para copiar o diretório com -a, instalar o Certbot, apontar o DNS e executar certbot renew --dry-run antes da virada.
O timer de renovação não acompanha você ao reconstruir o servidor ou migrar para um novo LTS. Após qualquer migração, restauração de snapshot ou upgrade de distro, execute systemctl list-timers 'certbot*' e um --dry-run. Pular essa etapa faz um site ficar offline 89 dias depois, às 3h da manhã, em um certificado que todos assumiram que seria renovado automaticamente.
Tudo isso assume uma máquina sob seu controle, com IP público e porta 80 aberta para a internet — em outras palavras, um VPS. A mecânica acima é idêntica em qualquer um deles.
Os mesmos passos de certificado aplicam-se no Apache em vez de nginx, e quando um certificado público não é uma opção, um certificado self-signed no Ubuntu atende serviços internos.
FAQ
Eu preciso da porta 80 aberta se meu site só atende HTTPS?
Sim, para o desafio HTTP-01. O Let's Encrypt sempre inicia a requisição de validação pela porta 80. O Certbot não possui implementação para TLS-ALPN-01, portanto, um firewall que abra apenas a 443 bloqueará tanto a emissão inicial quanto qualquer renovação automática posterior. Um redirecionamento da porta 80 para HTTPS funciona — a validação segue o redirecionamento. A única forma de ignorar a porta 80 completamente é usando DNS-01 com um plugin do provedor.
apt ou snap — qual Certbot eu devo instalar para nginx no Ubuntu 24.04?
Use o apt. O sudo apt install certbot python3-certbot-nginx fornece o Certbot 2.9.0 no Ubuntu 24.04, que é atual o suficiente para tudo neste guia, recebe patches de segurança via unattended-upgrades e não requer o snapd. Escolha o snap apenas se você precisar da versão mais recente imediatamente ou de um plugin de DNS distribuído exclusivamente como snap. Em ambos os casos, escolha apenas um: duas instalações significam dois timers de renovação apontando para a mesma árvore /etc/letsencrypt, e o que for esquecido causará falhas.
O Certbot pode emitir um certificado wildcard para nginx?
Apenas via DNS-01. Um wildcard como *.example.com não possui um hostname único para buscar o arquivo de desafio, portanto, --nginx, --webroot e --standalone não funcionam. Instale o plugin para o seu provedor de DNS, coloque um token de API com escopo limitado em um arquivo de credenciais restrito ao root e execute o certbot certonly --dns-cloudflare -d example.com -d '*.example.com', usando aspas no wildcard para evitar o globbing do shell.
Por que o nginx ainda serve o certificado antigo após uma renovação bem-sucedida?
O nginx mantém o certificado na memória e não detecta o novo arquivo no disco até que seja recarregado. O certbot --nginx recarrega para você, mas as execuções --webroot e --standalone não, então uma renovação pode ter sucesso enquanto o navegador ainda exibe um certificado prestes a expirar. Coloque um script executável em /etc/letsencrypt/renewal-hooks/deploy/ que execute o nginx -t && systemctl reload nginx; ele será acionado após cada renovação bem-sucedida.
O certbot renew --dry-run prova que a renovação funcionará?
Em grande parte. Ele executa o desafio real contra o ambiente de staging — mesmo firewall, mesmo DNS, mesmo caminho de código — sem custo de rate-limit e sem gravar nada no disco. Um sucesso significa que a parte de rede está correta. No entanto, ele não prova de forma confiável se o seu deploy hook será acionado. Teste isso separadamente: execute o script de hook manualmente e verifique o sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.