MV Melhor VPS

Guia

Como configurar SSL grátis com Let's Encrypt e Nginx

Configure SSL gratuito com Let's Encrypt e Nginx na VPS, ative HTTPS, teste a renovação automática e resolva erros comuns de certificado com segurança.

Revisão editorial: Concluída
⏱ Tempo estimado: 20-30 minutos
📊 Dificuldade: Intermediário

O que você vai aprender

Neste guia, você vai proteger um site servido pelo Nginx com um certificado SSL gratuito do Let’s Encrypt. O processo usa o Certbot, cliente oficial recomendado pela Electronic Frontier Foundation para solicitar e renovar certificados.

Ao concluir, você saberá:

  • Verificar se o domínio aponta para o endereço IPv4 correto da VPS.
  • Instalar o Nginx, o Certbot e o plugin de integração no Ubuntu ou Debian.
  • Criar um bloco de servidor funcional para o domínio.
  • Fazer backup da configuração antes de permitir alterações automáticas.
  • Liberar as portas HTTP e HTTPS sem bloquear o acesso SSH.
  • Solicitar um certificado e redirecionar o tráfego HTTP para HTTPS.
  • Testar a renovação automática antes de depender dela em produção.

O procedimento serve para sites institucionais, aplicações e APIs. Em instalações WordPress, consulte também as opções de VPS para WordPress, principalmente se você ainda estiver dimensionando memória, armazenamento e processamento para o projeto.

Pré-requisitos

Você precisa de uma VPS com Ubuntu 22.04, Ubuntu 24.04, Debian 11 ou Debian 12. Os comandos deste guia usam o gerenciador de pacotes apt. Em distribuições baseadas em RHEL, como AlmaLinux e Rocky Linux, os nomes dos pacotes e o gerenciamento do firewall são diferentes.

Prepare os seguintes itens antes de começar:

  • Acesso SSH à VPS com um usuário que possa executar sudo.
  • Um domínio registrado e acesso ao painel DNS.
  • Um registro DNS do tipo A apontando para o IPv4 público da VPS.
  • Portas TCP 80 e 443 liberadas no firewall do provedor.
  • Nginx instalado ou permissão para instalá-lo.
  • Um endereço de e-mail válido para avisos do Let’s Encrypt.

Conecte-se ao servidor. Troque o usuário e o endereço IP pelos dados fornecidos pelo seu provedor:

ssh [email protected]

Quando a conexão funcionar, você verá um prompt semelhante a:

usuario@vps:~$

O endereço 203.0.113.10 pertence a uma faixa reservada para documentação. Use o IP público real da sua VPS. Se a aplicação processa pagamentos, revise também os requisitos de infraestrutura para uma VPS destinada a loja virtual, pois HTTPS é apenas uma parte da proteção necessária.

Passo 1: Verificar o domínio e o DNS

O Let’s Encrypt precisa acessar seu domínio pela internet para confirmar que a VPS controla o site. Se o DNS apontar para outro servidor, a emissão falhará ou o certificado poderá ser instalado na máquina errada.

Instale as ferramentas usadas na consulta de DNS:

sudo apt update
sudo apt install -y dnsutils curl

O final da instalação deve apresentar algo semelhante a:

Setting up dnsutils ...
Processing triggers for man-db ...

Agora armazene o domínio em uma variável. Digite somente o nome, sem http://, https:// ou barras:

read -rp 'Digite o domínio, como site.seudominio.com.br: ' DOMAIN
export DOMAIN
printf 'Domínio selecionado: %s\n' "$DOMAIN"

A saída confirma exatamente o valor que será usado nos próximos comandos:

Domínio selecionado: site.seudominio.com.br

Compare o IPv4 público da VPS com o registro A retornado pelo DNS:

VPS_IP=$(curl -4 -s https://ifconfig.co)
DNS_IP=$(dig +short A "$DOMAIN" | tail -n 1)
printf 'IPv4 da VPS: %s\nIPv4 no DNS: %s\n' "$VPS_IP" "$DNS_IP"

O resultado deve mostrar o mesmo endereço nas duas linhas:

IPv4 da VPS: 198.51.100.24
IPv4 no DNS: 198.51.100.24

Os endereços acima são apenas uma representação do formato esperado. No servidor, os dois valores precisam corresponder ao IP real. Se IPv4 no DNS aparecer vazio ou diferente, corrija o registro A no provedor DNS e aguarde a propagação antes de continuar.

Verifique também se existe um registro IPv6:

dig +short AAAA "$DOMAIN"

Uma saída vazia significa que não existe registro AAAA. Se aparecer um IPv6, confirme que ele pertence à VPS e que o Nginx aceita conexões IPv6. Um registro AAAA incorreto pode fazer a validação falhar mesmo quando o registro A está certo.

Passo 2: Instalar Nginx e Certbot

Com o DNS confirmado, instale o servidor web, o cliente Certbot e o plugin que altera a configuração do Nginx. O plugin permite que o Certbot encontre o domínio, instale o certificado e crie o redirecionamento para HTTPS.

Execute:

sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx

Em uma instalação bem-sucedida, o apt encerra com mensagens semelhantes a estas:

Setting up nginx ...
Setting up certbot ...
Setting up python3-certbot-nginx ...
Processing triggers for systemd ...

Confira as versões instaladas:

nginx -v
certbot --version

Você verá uma resposta parecida com:

nginx version: nginx/1.24.0
certbot 2.9.0

Os números podem variar conforme a versão do sistema operacional. O ponto necessário é que os dois comandos terminem sem mensagens como command not found.

Inicie o Nginx e configure sua inicialização automática junto com o sistema:

sudo systemctl enable --now nginx
sudo systemctl status nginx --no-pager

A linha principal do status deve indicar que o serviço está ativo:

Active: active (running)

Faça ainda um teste de sintaxe antes de editar qualquer arquivo:

sudo nginx -t

O resultado esperado é:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Se o serviço não iniciar, não avance para a emissão. Execute sudo journalctl -u nginx -n 50 --no-pager e corrija o erro indicado. O Certbot depende de uma configuração válida para localizar e modificar o bloco do domínio.

Passo 3: Configurar o site e liberar HTTPS

Antes de alterar o Nginx, crie um backup completo da configuração. O comando abaixo apenas compacta os arquivos atuais e não remove o conteúdo original:

BACKUP_FILE="$HOME/nginx-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
sudo tar -czf "$BACKUP_FILE" /etc/nginx
sudo ls -lh "$BACKUP_FILE"

A verificação deve mostrar um arquivo compactado:

-rw-r--r-- 1 root root 18K Oct 5 14:30 /home/usuario/nginx-backup-20261005-143000.tar.gz

Se você já possui uma aplicação em produção, não substitua o bloco de servidor sem analisar a configuração existente. Localize-o com sudo nginx -T | grep -n server_name. Para uma instalação nova, crie o diretório e uma página simples:

sudo mkdir -p "/var/www/$DOMAIN/html"
printf '<!doctype html><html><body><h1>HTTPS pronto para %s</h1></body></html>\n' "$DOMAIN" | sudo tee "/var/www/$DOMAIN/html/index.html" >/dev/null
sudo chown -R www-data:www-data "/var/www/$DOMAIN"

Crie o bloco HTTP do domínio:

sudo tee "/etc/nginx/sites-available/$DOMAIN" >/dev/null <<EOF
server {
    listen 80;
    listen [::]:80;

    server_name $DOMAIN;
    root /var/www/$DOMAIN/html;
    index index.html;

    location / {
        try_files \$uri \$uri/ =404;
    }
}
EOF

sudo test -L "/etc/nginx/sites-enabled/$DOMAIN" || sudo ln -s "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/$DOMAIN"
sudo nginx -t
sudo systemctl reload nginx

A validação precisa terminar com:

nginx: configuration file /etc/nginx/nginx.conf test is successful

Agora confirme a porta usada pelo SSH antes de habilitar o UFW:

sudo ss -tlnp | grep sshd

Você normalmente verá 0.0.0.0:22. Se o SSH usa outra porta, libere essa porta explicitamente antes de ativar o firewall.

Aviso: habilitar o UFW sem uma regra SSH correta pode interromper sua conexão remota. Mantenha a sessão atual aberta até concluir os testes.

Para SSH na porta 22, execute:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable
sudo ufw status

O status esperado inclui as portas web e SSH:

OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
Status: active

Passo 4: Emitir o certificado Let’s Encrypt

Teste o acesso HTTP antes de solicitar o certificado. Isso confirma que DNS, Nginx e firewall estão funcionando juntos:

curl -I "http://$DOMAIN"

Antes da instalação do certificado, a resposta costuma começar assim:

HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html

Se houver timeout, corrija a conectividade da porta 80 antes de continuar. A autoridade certificadora usa essa porta no desafio HTTP-01.

Solicite o certificado com o plugin do Nginx:

sudo certbot --nginx -d "$DOMAIN"

O Certbot solicitará um endereço de e-mail, a aceitação dos termos de serviço e uma escolha sobre compartilhamento de contato. Nas versões que perguntam sobre redirecionamento, selecione a opção para encaminhar HTTP para HTTPS.

Uma emissão bem-sucedida apresenta uma mensagem semelhante a:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/site.seudominio.com.br/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/site.seudominio.com.br/privkey.pem
Successfully deployed certificate for site.seudominio.com.br to /etc/nginx/sites-enabled/site.seudominio.com.br

O Certbot modificará o bloco do Nginx para usar a porta 443, os arquivos do certificado e o redirecionamento HTTPS. Verifique imediatamente a sintaxe e o serviço:

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl is-active nginx

O último comando deve retornar:

active

Liste os certificados gerenciados:

sudo certbot certificates

A saída mostrará o nome, os domínios incluídos e a data de expiração:

Certificate Name: site.seudominio.com.br
Domains: site.seudominio.com.br
Expiry Date: 2027-01-03
Certificate Path: /etc/letsencrypt/live/site.seudominio.com.br/fullchain.pem

Para incluir www, crie primeiro o registro DNS correspondente. Depois, expanda o certificado com sudo certbot --nginx -d "$DOMAIN" -d "www.$DOMAIN". Não inclua um nome que ainda não resolve para a VPS.

Passo 5: Configurar e testar a renovação automática

Os certificados do Let’s Encrypt têm validade curta, normalmente 90 dias. Os pacotes do Certbot fornecidos pelo Ubuntu e Debian costumam instalar um timer do systemd que executa verificações periódicas. Você não precisa criar uma tarefa cron duplicada.

Consulte o timer:

sudo systemctl status certbot.timer --no-pager
sudo systemctl list-timers certbot.timer --no-pager

O status esperado contém estas informações:

Loaded: loaded
Active: active (waiting)
Trigger: Mon 2026-10-05 22:14:00 UTC

Se o timer estiver instalado, mas inativo, habilite-o:

sudo systemctl enable --now certbot.timer
sudo systemctl is-enabled certbot.timer

A resposta deve ser:

enabled

Agora faça uma simulação completa de renovação. O teste contata o ambiente de homologação do Let’s Encrypt e valida o fluxo sem substituir o certificado válido:

sudo certbot renew --dry-run

A operação pode levar alguns segundos. O resultado correto termina com:

Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/site.seudominio.com.br/fullchain.pem (success)

Não considere o trabalho concluído se o teste falhar. Uma renovação quebrada pode passar despercebida até o navegador começar a rejeitar o certificado.

Confirme também que o Nginx recarrega sua configuração após a renovação:

sudo grep -R "deploy_hook\|nginx" /etc/letsencrypt/renewal-hooks /etc/letsencrypt/renewal 2>/dev/null || true
sudo systemctl reload nginx
sudo nginx -t

A ausência de saída no primeiro comando não significa necessariamente erro. O instalador do Certbot pode armazenar as ações de implantação na configuração interna do plugin. O teste decisivo continua sendo certbot renew --dry-run, seguido de uma configuração válida do Nginx.

Verificação e testes

Faça uma verificação externa pelo próprio terminal. Primeiro, confirme que HTTP redireciona para HTTPS:

curl -I "http://$DOMAIN"

A resposta esperada contém um código de redirecionamento e o destino seguro:

HTTP/1.1 301 Moved Permanently
Location: https://site.seudominio.com.br/

Teste a resposta HTTPS:

curl -I "https://$DOMAIN"

O servidor deve responder sem erro de certificado:

HTTP/2 200
server: nginx
content-type: text/html

Consulte diretamente o certificado apresentado na porta 443:

openssl s_client -connect "$DOMAIN:443" -servername "$DOMAIN" </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates

Você verá o domínio, o emissor e o período de validade:

subject=CN = site.seudominio.com.br
issuer=C = US, O = Let's Encrypt, CN = E6
notBefore=Oct  5 12:00:00 2026 GMT
notAfter=Jan  3 11:59:59 2027 GMT

Por fim, abra https://seu-dominio em uma janela anônima do navegador. Verifique o cadeado, carregue páginas internas e teste formulários ou chamadas de API. Se a página abrir, mas exibir alerta de conteúdo misto, o certificado está ativo e existem recursos carregados por URLs http:// dentro da aplicação.

Troubleshooting

O Certbot retorna timeout ou unauthorized

Confirme DNS, porta 80 e resposta HTTP:

dig +short A "$DOMAIN"
sudo ss -tlnp | grep ':80'
curl -I "http://$DOMAIN/.well-known/acme-challenge/test"

Um 404 Not Found no último teste é aceitável, pois o arquivo não existe. Timeout ou conexão recusada indicam firewall, regra de rede do provedor ou Nginx indisponível. Se você usa proxy de CDN, desative temporariamente o proxy ou siga o método DNS recomendado pelo serviço.

O Nginx informa conflito de server_name

Localize blocos duplicados:

sudo nginx -T 2>&1 | grep -n "server_name.*$DOMAIN"

Mantenha apenas um bloco ativo para o domínio na porta 80 durante a emissão. Antes de remover ou desativar qualquer arquivo, copie-o para o diretório pessoal. Depois, execute sudo nginx -t e só recarregue o serviço quando o teste for bem-sucedido.

A porta 443 continua inacessível

Verifique se o Nginx escuta a porta e se o firewall permite conexões:

sudo ss -tlnp | grep ':443'
sudo ufw status
sudo nginx -t

Se o Nginx estiver ouvindo e o UFW mostrar Nginx Full ALLOW, confira o firewall externo no painel da VPS. Muitos provedores possuem regras de rede separadas do firewall do sistema.

O dry-run falha por limite ou erro de plugin

Consulte o log detalhado:

sudo tail -n 80 /var/log/letsencrypt/letsencrypt.log
sudo certbot plugins

A lista precisa mostrar o plugin nginx. Caso ele não apareça, reinstale python3-certbot-nginx com sudo apt install --reinstall python3-certbot-nginx e repita o teste. Evite várias emissões reais consecutivas, pois o Let’s Encrypt aplica limites. Use --dry-run durante o diagnóstico.

O navegador mostra conteúdo misto

Procure referências HTTP no código servido:

grep -R "http://" "/var/www/$DOMAIN" | head -n 20

Atualize URLs de imagens, scripts, folhas de estilo e endpoints para HTTPS. Em aplicações com proxy reverso, configure também o reconhecimento do cabeçalho X-Forwarded-Proto para impedir redirecionamentos repetidos.

Próximos passos

Depois de confirmar o HTTPS, automatize backups do diretório /etc/letsencrypt e da configuração do Nginx. Proteja essas cópias, pois a pasta contém chaves privadas. Configure monitoramento para receber alertas sobre indisponibilidade e expiração do certificado.

Você também pode adicionar cabeçalhos de segurança, limitar protocolos TLS antigos e configurar HSTS depois de confirmar que todos os subdomínios funcionam exclusivamente por HTTPS. Não ative HSTS com includeSubDomains por impulso, pois uma configuração incorreta pode bloquear subdomínios ainda sem certificado.

Se a VPS hospeda ambientes de desenvolvimento, pipelines ou APIs, veja os critérios de uma VPS para desenvolvedores e mantenha homologação separada da produção. Finalize criando um teste periódico com curl ou uma ferramenta de monitoramento externo.

Perguntas frequentes

O certificado do Let's Encrypt é realmente gratuito?

Sim. O Let's Encrypt é uma autoridade certificadora pública e gratuita que emite certificados validados por domínio. O certificado oferece a mesma criptografia TLS usada por certificados pagos equivalentes. A diferença costuma estar em serviços comerciais adicionais, como suporte, garantia financeira ou validação organizacional. Para sites, APIs e lojas que precisam de validação do domínio, o certificado gratuito funciona normalmente. Você ainda precisa proteger a chave privada, manter o servidor atualizado e confirmar que a renovação automática está funcionando.

Preciso manter a porta 80 aberta depois de ativar HTTPS?

Se você usa o desafio HTTP-01 do Certbot, mantenha a porta 80 acessível para as renovações e para redirecionar visitantes de HTTP para HTTPS. Fechar essa porta pode fazer a próxima validação falhar, dependendo do método configurado. A porta 80 não precisa servir conteúdo sem criptografia, pois o Nginx pode responder apenas com redirecionamento para HTTPS. Se sua política exige bloquear HTTP, configure um desafio DNS-01 e teste a automação antes de alterar o firewall.

Posso emitir um certificado para o domínio e para www?

Sim, desde que os dois nomes resolvam publicamente para o servidor responsável pela validação. Crie registros DNS para o domínio principal e para `www`, confirme ambos com `dig` e execute o Certbot usando duas opções `-d`. O certificado resultante incluirá os dois nomes na extensão Subject Alternative Name. Se apenas um deles possuir DNS correto, a solicitação inteira poderá falhar. Também configure o Nginx para decidir qual versão é canônica e redirecionar a outra, evitando conteúdo duplicado.

O Certbot reinicia o Nginx durante a renovação?

Normalmente, o Certbot recarrega o Nginx após instalar um certificado renovado. Um reload relê a configuração e os arquivos TLS sem encerrar de forma abrupta as conexões existentes. Isso é diferente de parar e iniciar o serviço. O comportamento exato depende do plugin e dos hooks configurados. Use `certbot renew --dry-run` para testar todo o fluxo e consulte os logs em `/var/log/letsencrypt/`. Depois do teste, execute `nginx -t` e confirme que o serviço permanece com estado `active`.

Como usar Let's Encrypt quando o Nginx está em um contêiner?

Você precisa definir onde o desafio ACME será respondido e como os arquivos do certificado chegarão ao contêiner. Uma abordagem usa o Certbot no host, um diretório compartilhado para `/.well-known/acme-challenge` e volumes somente leitura com `/etc/letsencrypt`. Outra opção executa Certbot em um contêiner separado. Em ambos os casos, planeje um mecanismo para recarregar o Nginx após a renovação. Não copie manualmente apenas o arquivo final, pois os caminhos em `/etc/letsencrypt/live` são links para versões arquivadas.

Por que o navegador ainda mostra conexão insegura após instalar o certificado?

Primeiro, confirme se o navegador acessa exatamente o nome incluído no certificado. Um certificado para o domínio principal não cobre automaticamente `www` nem outros subdomínios. Depois, teste o servidor com `openssl s_client` e confira se o Nginx apresenta o certificado correto. Se o cadeado aparece com aviso na página, procure conteúdo misto, como imagens ou scripts carregados por HTTP. Cache do navegador, proxy reverso e CDN também podem apresentar um certificado antigo, então teste em janela anônima e diretamente no IP de origem quando possível.

Fontes consultadas