{"id":100,"date":"2026-06-14T21:02:43","date_gmt":"2026-06-14T21:02:43","guid":{"rendered":"https:\/\/shattered.io\/pt\/2026\/06\/14\/https-nginx-lets-encrypt\/"},"modified":"2026-06-14T21:04:15","modified_gmt":"2026-06-14T21:04:15","slug":"https-nginx-lets-encrypt","status":"publish","type":"post","link":"https:\/\/shattered.io\/pt\/2026\/06\/14\/https-nginx-lets-encrypt\/","title":{"rendered":"HTTPS no Nginx com Let&#8217;s Encrypt: 12 Passos [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Servir um site sobre HTTP simples deixou de ser uma op\u00e7\u00e3o em 2026. Os navegadores marcam qualquer p\u00e1gina sem cadeado como &#8220;N\u00e3o seguro&#8221;, o Google penaliza no ranking e qualquer pessoa na mesma rede consegue ler o tr\u00e1fego em texto claro. A boa not\u00edcia: configurar HTTPS no <strong>Nginx<\/strong> com certificados gratuitos da Let&#8217;s Encrypt demora menos de 30 minutos e, feito corretamente, d\u00e1-lhe uma nota A+ no SSL Labs com TLS 1.3, HSTS e cabe\u00e7alhos de seguran\u00e7a modernos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Este tutorial leva-o do servidor vazio at\u00e9 uma configura\u00e7\u00e3o de produ\u00e7\u00e3o endurecida em 12 passos, com blocos de c\u00f3digo prontos a copiar, exemplos de sa\u00edda reais, armadilhas comuns e um guia de resolu\u00e7\u00e3o de problemas. No fim tem um ficheiro de configura\u00e7\u00e3o completo do <strong>Nginx<\/strong> e ainda mostramos como ativar a troca de chaves p\u00f3s-qu\u00e2ntica que o Chrome e o Firefox j\u00e1 usam por predefini\u00e7\u00e3o.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"porque-configurar-https-no-nginx-em-2026\">Porque configurar HTTPS no Nginx em 2026<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O HTTPS protege tr\u00eas coisas ao mesmo tempo: a confidencialidade (ningu\u00e9m l\u00ea os dados), a integridade (ningu\u00e9m os altera em tr\u00e2nsito) e a autenticidade (o utilizador sabe que fala com o servidor certo). Sem TLS, uma palavra-passe escrita num formul\u00e1rio viaja em texto claro por todos os routers entre o cliente e o servidor. Com TLS 1.3, esse mesmo tr\u00e1fego fica cifrado com cifras autenticadas como a AES-256-GCM ou a ChaCha20-Poly1305.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A Let&#8217;s Encrypt mudou o jogo ao emitir certificados gratuitos e automatizados. A autoridade de certifica\u00e7\u00e3o j\u00e1 passou a marca de v\u00e1rios milhares de milh\u00f5es de certificados emitidos e, em 2025, anunciou duas altera\u00e7\u00f5es importantes: o fim das mensagens de aviso de expira\u00e7\u00e3o por email e o fim do suporte a OCSP, com migra\u00e7\u00e3o para listas de revoga\u00e7\u00e3o (CRL). Para si, isto significa uma coisa simples: automatize a renova\u00e7\u00e3o e n\u00e3o dependa de OCSP stapling como rede de seguran\u00e7a.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Os certificados da Let&#8217;s Encrypt s\u00e3o v\u00e1lidos por 90 dias. Esse prazo curto \u00e9 intencional: limita a janela de exposi\u00e7\u00e3o se uma chave for comprometida e obriga a automatizar. O <strong>certbot<\/strong>, o cliente oficial mantido pela EFF, trata da emiss\u00e3o e da renova\u00e7\u00e3o sem interven\u00e7\u00e3o manual. Em 2025 a Let&#8217;s Encrypt come\u00e7ou tamb\u00e9m a oferecer certificados de vida curta (6 dias) e certificados para endere\u00e7os IP, mas para a maioria dos sites o ciclo cl\u00e1ssico de 90 dias com renova\u00e7\u00e3o autom\u00e1tica continua a ser o caminho recomendado.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O <strong>Nginx<\/strong> \u00e9 o servidor web ideal para este trabalho. \u00c9 leve, r\u00e1pido e processa milhares de liga\u00e7\u00f5es em simult\u00e2neo com baixo consumo de mem\u00f3ria. Combinado com o TLS 1.3 (definido no RFC 8446), o aperto de m\u00e3o (handshake) precisa de apenas uma ida e volta na rede, cortando a lat\u00eancia face ao TLS 1.2. O resultado \u00e9 um site mais seguro e, ao mesmo tempo, mais r\u00e1pido.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"como-funciona-o-aperto-de-mao-tls-1-3\">Como funciona o aperto de m\u00e3o TLS 1.3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Perceber o que acontece nos bastidores ajuda a depurar quando algo falha. Quando um navegador abre <code>https:\/\/exemplo.pt<\/code>, cliente e servidor executam um aperto de m\u00e3o (handshake) antes de trocar qualquer byte de conte\u00fado. No TLS 1.3, esse processo foi simplificado de forma radical face \u00e0s vers\u00f5es anteriores e precisa de uma \u00fanica ida e volta na rede (1-RTT), com suporte opcional a 0-RTT para sess\u00f5es retomadas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O cliente envia um <em>ClientHello<\/em> que j\u00e1 inclui a sua parte da troca de chaves de curva el\u00edptica (key share), as cifras que suporta e a extens\u00e3o SNI (Server Name Indication) a indicar qual o dom\u00ednio pretendido. O SNI \u00e9 importante quando v\u00e1rios sites partilham o mesmo IP: \u00e9 assim que o Nginx sabe qual o certificado a apresentar. O servidor responde com o <em>ServerHello<\/em>, a sua key share, o certificado e uma assinatura que prova que det\u00e9m a chave privada correspondente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A partir das duas key shares, ambos derivam o mesmo segredo partilhado atrav\u00e9s de Diffie-Hellman de curva el\u00edptica ef\u00e9mera (ECDHE). A palavra &#8220;ef\u00e9mera&#8221; \u00e9 a chave da confidencialidade futura (forward secrecy): como cada sess\u00e3o usa um par de chaves novo e descart\u00e1vel, comprometer a chave privada do servidor hoje n\u00e3o permite decifrar tr\u00e1fego capturado no passado. \u00c9 por isso que as cifras recomendadas come\u00e7am todas por ECDHE. O TLS 1.2 tamb\u00e9m suporta ECDHE, mas o TLS 1.3 torna-o obrigat\u00f3rio e elimina as antigas trocas de chaves est\u00e1ticas RSA, que n\u00e3o ofereciam forward secrecy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Depois de derivado o segredo, todo o tr\u00e1fego seguinte, incluindo o resto do pr\u00f3prio aperto de m\u00e3o, fica cifrado. No TLS 1.3 isto significa que o certificado do servidor j\u00e1 viaja cifrado, melhorando a privacidade. O resultado pr\u00e1tico \u00e9 um aperto de m\u00e3o mais r\u00e1pido e mais privado, e uma das raz\u00f5es pelas quais migrar para HTTPS moderno no <strong>Nginx<\/strong> melhora o desempenho em vez de o degradar.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pre-requisitos-e-versoes-necessarias\">Pr\u00e9-requisitos e vers\u00f5es necess\u00e1rias<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de come\u00e7ar, confirme que tem os componentes abaixo. As vers\u00f5es indicadas s\u00e3o as refer\u00eancias est\u00e1veis para 2026; quando estiver na d\u00favida, instale a vers\u00e3o mais recente dispon\u00edvel no reposit\u00f3rio da sua distribui\u00e7\u00e3o.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Componente<\/th><th>Vers\u00e3o m\u00ednima<\/th><th>Para que serve<\/th><\/tr><\/thead><tbody><tr><td>Servidor Linux<\/td><td>Ubuntu 24.04 LTS ou Debian 12<\/td><td>Sistema base com systemd<\/td><\/tr><tr><td>Nginx<\/td><td>1.26 ou superior<\/td><td>Servidor web e termina\u00e7\u00e3o TLS<\/td><\/tr><tr><td>OpenSSL<\/td><td>3.0 ou superior<\/td><td>Biblioteca criptogr\u00e1fica (TLS 1.3)<\/td><\/tr><tr><td>Certbot<\/td><td>vers\u00e3o mais recente (via snap)<\/td><td>Cliente ACME da Let&#8217;s Encrypt<\/td><\/tr><tr><td>Nome de dom\u00ednio<\/td><td>com registo DNS A\/AAAA<\/td><td>Valida\u00e7\u00e3o do certificado<\/td><\/tr><tr><td>Acesso root\/sudo<\/td><td>obrigat\u00f3rio<\/td><td>Editar configura\u00e7\u00f5es e portas<\/td><\/tr><tr><td>Portas 80 e 443<\/td><td>abertas na firewall<\/td><td>HTTP-01 e tr\u00e1fego HTTPS<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Um requisito frequentemente esquecido: o seu dom\u00ednio tem de apontar para o IP p\u00fablico do servidor <strong>antes<\/strong> de pedir o certificado. A Let&#8217;s Encrypt usa o desafio HTTP-01, que coloca um ficheiro tempor\u00e1rio em <code>\/.well-known\/acme-challenge\/<\/code> e verifica-o atrav\u00e9s do seu dom\u00ednio. Se o DNS ainda n\u00e3o propagou, a emiss\u00e3o falha. Verifique a resolu\u00e7\u00e3o com <code>dig +short exemplo.pt<\/code> e confirme que devolve o IP correto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Confirme tamb\u00e9m a vers\u00e3o do Nginx e do OpenSSL antes de avan\u00e7ar. O TLS 1.3 s\u00f3 funciona com OpenSSL 1.1.1 ou superior; com OpenSSL 3.x ganha acesso \u00e0s cifras mais recentes e \u00e0 troca de chaves h\u00edbrida p\u00f3s-qu\u00e2ntica que abordamos no fim.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nginx -v\n# nginx version: nginx\/1.26.2\n\nopenssl version\n# OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13)\n\ndig +short exemplo.pt\n# 203.0.113.10<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-1-atualizar-o-sistema-e-instalar-o-nginx\">Passo 1: Atualizar o sistema e instalar o Nginx<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Comece sempre por atualizar os pacotes. Um sistema desatualizado \u00e9 a forma mais comum de deixar uma porta aberta sem perceber. Em Ubuntu ou Debian, execute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update &amp;&amp; sudo apt upgrade -y\nsudo apt install nginx -y<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Depois da instala\u00e7\u00e3o, o Nginx arranca automaticamente. Verifique o estado do servi\u00e7o para confirmar que est\u00e1 ativo e a escutar na porta 80:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl status nginx\n# \u25cf nginx.service - A high performance web server and a reverse proxy server\n#      Loaded: loaded (\/lib\/systemd\/system\/nginx.service; enabled; preset: enabled)\n#      Active: active (running) since Sat 2026-03-14 10:22:01 WET; 5s ago<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Abra o IP do servidor no navegador. Deve ver a p\u00e1gina predefinida &#8220;Welcome to nginx!&#8221;. Se n\u00e3o vir, o problema est\u00e1 quase sempre na firewall (Passo 2) ou no facto de outro servi\u00e7o j\u00e1 ocupar a porta 80. Use <code>sudo ss -tlnp | grep ':80'<\/code> para confirmar qual o processo que est\u00e1 a escutar nessa porta. Garanta ainda que o servi\u00e7o arranca no boot com <code>sudo systemctl enable nginx<\/code>, embora os pacotes de Ubuntu e Debian j\u00e1 o fa\u00e7am por predefini\u00e7\u00e3o.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-2-configurar-a-firewall-com-ufw\">Passo 2: Configurar a firewall com ufw<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O TLS n\u00e3o serve de nada se a porta 443 estiver fechada. Em Ubuntu, a forma mais simples de gerir a firewall \u00e9 o <code>ufw<\/code>. O pacote do Nginx regista perfis prontos a usar. Ative a porta de SSH primeiro (caso contr\u00e1rio arrisca-se a perder o acesso ao servidor) e s\u00f3 depois o tr\u00e1fego web.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw allow OpenSSH\nsudo ufw allow 'Nginx Full'\nsudo ufw enable\nsudo ufw status<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O perfil &#8220;Nginx Full&#8221; abre as portas 80 (HTTP) e 443 (HTTPS). Precisa das duas: a 80 serve para o desafio HTTP-01 da Let&#8217;s Encrypt e para o redirecionamento para HTTPS, e a 443 serve o tr\u00e1fego cifrado. A sa\u00edda esperada do comando de estado \u00e9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Status: active\n\nTo                         Action      From\n--                         ------      ----\nOpenSSH                    ALLOW       Anywhere\nNginx Full                 ALLOW       Anywhere\nOpenSSH (v6)               ALLOW       Anywhere (v6)\nNginx Full (v6)            ALLOW       Anywhere (v6)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se usar um fornecedor de nuvem como a OVH, a Hetzner ou a AWS, lembre-se de que existe muitas vezes uma firewall externa (security group) al\u00e9m do <code>ufw<\/code>. Tem de abrir as portas 80 e 443 nos dois s\u00edtios. Esta dupla camada \u00e9 uma fonte cl\u00e1ssica de frustra\u00e7\u00e3o: o <code>ufw<\/code> diz que est\u00e1 tudo aberto, mas a liga\u00e7\u00e3o continua a falhar porque o grupo de seguran\u00e7a da nuvem bloqueia o tr\u00e1fego.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-3-criar-o-server-block-do-dominio\">Passo 3: Criar o server block do dom\u00ednio<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Antes de pedir o certificado, o Nginx tem de saber responder pelo seu dom\u00ednio. Crie um ficheiro de configura\u00e7\u00e3o dedicado em <code>\/etc\/nginx\/sites-available\/<\/code>. Esta separa\u00e7\u00e3o por ficheiros facilita a gest\u00e3o de v\u00e1rios sites no mesmo servidor.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/var\/www\/exemplo.pt\/html\necho \"&lt;h1&gt;Site servido por Nginx&lt;\/h1&gt;\" | sudo tee \/var\/www\/exemplo.pt\/html\/index.html\nsudo nano \/etc\/nginx\/sites-available\/exemplo.pt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Coloque o seguinte conte\u00fado no ficheiro. Repare que, por agora, s\u00f3 configuramos o HTTP na porta 80. O <strong>certbot<\/strong> vai adicionar automaticamente a parte do HTTPS no passo seguinte.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server {\n    listen 80;\n    listen [::]:80;\n\n    server_name exemplo.pt www.exemplo.pt;\n    root \/var\/www\/exemplo.pt\/html;\n    index index.html;\n\n    location \/ {\n        try_files $uri $uri\/ =404;\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Ative o site criando uma liga\u00e7\u00e3o simb\u00f3lica para <code>sites-enabled<\/code>, teste a sintaxe e recarregue o Nginx. O comando <code>nginx -t<\/code> \u00e9 o seu melhor amigo: corra-o sempre antes de recarregar, porque apanha erros de sintaxe que de outra forma derrubariam o servidor.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ln -s \/etc\/nginx\/sites-available\/exemplo.pt \/etc\/nginx\/sites-enabled\/\nsudo nginx -t\n# nginx: configuration file \/etc\/nginx\/nginx.conf test is successful\nsudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-4-instalar-o-certbot-da-lets-encrypt\">Passo 4: Instalar o Certbot da Let&#8217;s Encrypt<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A EFF recomenda instalar o <strong>certbot<\/strong> atrav\u00e9s do snap, porque garante sempre a vers\u00e3o mais recente com as \u00faltimas corre\u00e7\u00f5es de seguran\u00e7a e suporte aos protocolos ACME atuais. O pacote <code>apt<\/code> existe, mas costuma estar v\u00e1rias vers\u00f5es atrasado.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo snap install core; sudo snap refresh core\nsudo snap install --classic certbot\nsudo ln -s \/snap\/bin\/certbot \/usr\/bin\/certbot\ncertbot --version\n# certbot 3.x.x<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se preferir evitar o snap (por exemplo, em Debian minimal), pode instalar via <code>apt<\/code> com <code>sudo apt install certbot python3-certbot-nginx -y<\/code>. O fluxo dos passos seguintes \u00e9 id\u00eantico. A \u00fanica diferen\u00e7a pr\u00e1tica \u00e9 que com o snap recebe atualiza\u00e7\u00f5es autom\u00e1ticas do pr\u00f3prio certbot, o que \u00e9 uma vantagem de seguran\u00e7a em servidores de produ\u00e7\u00e3o que ficam meses sem manuten\u00e7\u00e3o manual.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O <strong>certbot<\/strong> usa o protocolo ACME para provar \u00e0 Let&#8217;s Encrypt que controla o dom\u00ednio. O m\u00e9todo predefinido, HTTP-01, coloca um ficheiro num caminho conhecido e a autoridade verifica-o pela internet p\u00fablica. Existe tamb\u00e9m o desafio DNS-01, \u00fatil para certificados wildcard, mas exige acesso \u00e0 API do seu fornecedor de DNS e fica fora do \u00e2mbito deste tutorial b\u00e1sico.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-5-emitir-o-certificado-tls\">Passo 5: Emitir o certificado TLS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Com o plugin de Nginx, a emiss\u00e3o \u00e9 praticamente autom\u00e1tica. O <strong>certbot<\/strong> l\u00ea os seus server blocks, identifica os dom\u00ednios, obt\u00e9m o certificado e edita a configura\u00e7\u00e3o para ativar o HTTPS. Execute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo certbot --nginx -d exemplo.pt -d www.exemplo.pt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Na primeira execu\u00e7\u00e3o, o certbot pede o seu email (para notifica\u00e7\u00f5es urgentes, n\u00e3o comerciais) e que aceite os termos de servi\u00e7o. A sa\u00edda de sucesso tem este aspeto:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Successfully received certificate.\nCertificate is saved at: \/etc\/letsencrypt\/live\/exemplo.pt\/fullchain.pem\nKey is saved at:         \/etc\/letsencrypt\/live\/exemplo.pt\/privkey.pem\nThis certificate expires on 2026-06-12.\nDeploying certificate\nSuccessfully deployed certificate for exemplo.pt to \/etc\/nginx\/sites-enabled\/exemplo.pt\nCongratulations! You have successfully enabled HTTPS on https:\/\/exemplo.pt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Repare nos ficheiros gerados. O <code>fullchain.pem<\/code> cont\u00e9m o seu certificado mais a cadeia interm\u00e9dia, e o <code>privkey.pem<\/code> \u00e9 a chave privada. Nunca partilhe nem versione a chave privada num reposit\u00f3rio Git. O certbot guarda tudo em <code>\/etc\/letsencrypt\/live\/<\/code> com liga\u00e7\u00f5es simb\u00f3licas que apontam sempre para a vers\u00e3o mais recente, o que torna a renova\u00e7\u00e3o transparente para o Nginx.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Abra agora <code>https:\/\/exemplo.pt<\/code> no navegador. Deve ver o cadeado fechado. Se clicar nele e inspecionar o certificado, ver\u00e1 que foi emitido por uma autoridade interm\u00e9dia da Let&#8217;s Encrypt e que \u00e9 v\u00e1lido por 90 dias. Parab\u00e9ns: o site j\u00e1 est\u00e1 cifrado. Mas a configura\u00e7\u00e3o predefinida do certbot \u00e9 apenas o ponto de partida. Os passos seguintes transformam-na numa configura\u00e7\u00e3o de produ\u00e7\u00e3o endurecida.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-6-ativar-tls-1-3-e-cifras-seguras\">Passo 6: Ativar TLS 1.3 e cifras seguras<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O certbot ativa o TLS, mas conv\u00e9m afinar manualmente os protocolos e as cifras. O objetivo em 2026 \u00e9 claro: ativar apenas TLS 1.2 e TLS 1.3, e desativar por completo o SSLv3, o TLS 1.0 e o TLS 1.1, que est\u00e3o obsoletos desde o RFC 8996 (2021). A configura\u00e7\u00e3o de refer\u00eancia da Mozilla, no perfil &#8220;intermediate&#8221;, continua a ser o padr\u00e3o da ind\u00fastria.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Edite o ficheiro do site e ajuste o bloco do <code>server<\/code> que escuta na porta 443 (criado pelo certbot). Adicione ou substitua as diretivas TLS:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    ssl_protocols TLSv1.2 TLSv1.3;\n    ssl_prefer_server_ciphers off;\n    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;\n\n    ssl_session_timeout 1d;\n    ssl_session_cache shared:MozSSL:10m;\n    ssl_session_tickets off;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note que no TLS 1.3 as cifras s\u00e3o fixas pela pr\u00f3pria norma (AES-128-GCM, AES-256-GCM e ChaCha20-Poly1305) e n\u00e3o s\u00e3o controladas pela diretiva <code>ssl_ciphers<\/code>. Essa lista aplica-se apenas ao TLS 1.2. A diretiva <code>ssl_prefer_server_ciphers off<\/code> deixa o cliente escolher, o que em 2026 \u00e9 a recomenda\u00e7\u00e3o porque os clientes modernos preferem ChaCha20 em dispositivos m\u00f3veis sem acelera\u00e7\u00e3o AES por hardware.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Protocolo<\/th><th>Estado em 2026<\/th><th>A\u00e7\u00e3o recomendada<\/th><\/tr><\/thead><tbody><tr><td>SSLv2 \/ SSLv3<\/td><td>Quebrado<\/td><td>Desativar sempre<\/td><\/tr><tr><td>TLS 1.0 \/ TLS 1.1<\/td><td>Obsoleto (RFC 8996)<\/td><td>Desativar<\/td><\/tr><tr><td>TLS 1.2<\/td><td>Seguro com cifras AEAD<\/td><td>Manter como m\u00ednimo<\/td><\/tr><tr><td>TLS 1.3<\/td><td>Recomendado (RFC 8446)<\/td><td>Ativar e preferir<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Teste e recarregue. Sempre que mexer no ficheiro, corra <code>sudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/code>. O <code>reload<\/code> aplica a nova configura\u00e7\u00e3o sem cortar liga\u00e7\u00f5es ativas, ao contr\u00e1rio do <code>restart<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-7-ativar-hsts-e-cabecalhos-de-seguranca\">Passo 7: Ativar HSTS e cabe\u00e7alhos de seguran\u00e7a<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O HSTS (HTTP Strict Transport Security) instrui o navegador a s\u00f3 comunicar por HTTPS com o seu dom\u00ednio, mesmo que o utilizador escreva <code>http:\/\/<\/code>. Isto bloqueia ataques de downgrade e de SSL stripping. Adicione o cabe\u00e7alho no bloco da porta 443:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    add_header Strict-Transport-Security \"max-age=63072000; includeSubDomains; preload\" always;\n    add_header X-Content-Type-Options \"nosniff\" always;\n    add_header X-Frame-Options \"SAMEORIGIN\" always;\n    add_header Referrer-Policy \"strict-origin-when-cross-origin\" always;\n    add_header Permissions-Policy \"geolocation=(), microphone=(), camera=()\" always;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O <code>max-age<\/code> de 63072000 segundos equivale a dois anos. A diretiva <code>preload<\/code> torna o dom\u00ednio eleg\u00edvel para a lista de pr\u00e9-carregamento dos navegadores, que aplica o HSTS antes mesmo da primeira visita. Cuidado: o <code>preload<\/code> \u00e9 um compromisso s\u00e9rio. Se mais tarde quiser servir algo por HTTP, vai ter problemas, porque remover um dom\u00ednio dessa lista demora semanas. Submeta-o em hstspreload.org s\u00f3 quando tiver a certeza de que todo o tr\u00e1fego, incluindo subdom\u00ednios, ser\u00e1 sempre HTTPS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A palavra-chave <code>always<\/code> no fim de cada cabe\u00e7alho \u00e9 essencial: garante que o cabe\u00e7alho \u00e9 enviado mesmo em respostas de erro (como 404 ou 500). Sem ela, uma p\u00e1gina de erro pode escapar sem o HSTS, abrindo uma pequena janela de ataque. Para um endurecimento mais profundo, considere ainda uma Content-Security-Policy (CSP), embora exija testes cuidadosos para n\u00e3o bloquear scripts leg\u00edtimos do seu site.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-8-forcar-o-redirecionamento-de-http-para-https\">Passo 8: For\u00e7ar o redirecionamento de HTTP para HTTPS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O certbot j\u00e1 costuma configurar o redirecionamento, mas vale a pena confirmar e perceber como funciona. O bloco da porta 80 deve apenas redirecionar tudo para HTTPS com um c\u00f3digo 301 (permanente), exceto o caminho do desafio ACME, que tem de continuar acess\u00edvel por HTTP para as renova\u00e7\u00f5es futuras.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>server {\n    listen 80;\n    listen [::]:80;\n    server_name exemplo.pt www.exemplo.pt;\n\n    location \/.well-known\/acme-challenge\/ {\n        root \/var\/www\/exemplo.pt\/html;\n    }\n\n    location \/ {\n        return 301 https:\/\/$host$request_uri;\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O c\u00f3digo 301 diz aos navegadores e motores de busca que a mudan\u00e7a \u00e9 permanente, o que preserva o valor de SEO das liga\u00e7\u00f5es antigas. Evite o 302 (tempor\u00e1rio) para este caso. Confirme o redirecionamento com <code>curl<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -I http:\/\/exemplo.pt\n# HTTP\/1.1 301 Moved Permanently\n# Location: https:\/\/exemplo.pt\/\n# Server: nginx\/1.26.2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Repare que o redirecionamento usa <code>$host<\/code> em vez de codificar o dom\u00ednio. Assim a mesma configura\u00e7\u00e3o serve <code>exemplo.pt<\/code> e <code>www.exemplo.pt<\/code> sem duplicar regras. Se quiser canonizar para uma das vers\u00f5es (por exemplo, sempre sem www), fa\u00e7a-o num server block separado para manter a l\u00f3gica clara.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-9-configurar-a-renovacao-automatica\">Passo 9: Configurar a renova\u00e7\u00e3o autom\u00e1tica<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Com certificados de 90 dias, a renova\u00e7\u00e3o autom\u00e1tica n\u00e3o \u00e9 opcional. Felizmente, a instala\u00e7\u00e3o via snap j\u00e1 cria um temporizador do systemd que tenta renovar duas vezes por dia. O certbot s\u00f3 renova de facto quando faltam 30 dias ou menos para a expira\u00e7\u00e3o, por isso pode correr com frequ\u00eancia sem sobrecarregar a Let&#8217;s Encrypt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Verifique o temporizador e fa\u00e7a um teste de renova\u00e7\u00e3o a seco (dry run), que simula todo o processo sem gastar os limites de emiss\u00e3o:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl list-timers | grep certbot\n# snap.certbot.renew.timer  Sun 2026-03-15 03:14:00 WET  ...\n\nsudo certbot renew --dry-run\n# Congratulations, all simulated renewals succeeded:\n#   \/etc\/letsencrypt\/live\/exemplo.pt\/fullchain.pem (success)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se o dry run passar, est\u00e1 tudo certo. Quando o certbot renova, recarrega o Nginx automaticamente atrav\u00e9s de um hook de deploy. Pode confirmar o hook em <code>\/etc\/letsencrypt\/renewal\/exemplo.pt.conf<\/code>. Caso tenha uma configura\u00e7\u00e3o personalizada, adicione um hook expl\u00edcito com <code>--deploy-hook \"systemctl reload nginx\"<\/code> para garantir que o servidor passa a usar o certificado novo sem interven\u00e7\u00e3o.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Uma boa pr\u00e1tica extra: configure uma monitoriza\u00e7\u00e3o externa que o avise se o certificado se aproximar da expira\u00e7\u00e3o. Como a Let&#8217;s Encrypt deixou de enviar emails de aviso em 2025, a responsabilidade de vigiar passou inteiramente para si. Ferramentas gratuitas de uptime conseguem alertar quando faltam, por exemplo, 14 dias.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-10-testar-a-configuracao-no-ssl-labs\">Passo 10: Testar a configura\u00e7\u00e3o no SSL Labs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Nunca confie numa configura\u00e7\u00e3o de TLS sem a testar de forma independente. O Qualys SSL Labs \u00e9 a ferramenta de refer\u00eancia: faz dezenas de verifica\u00e7\u00f5es e atribui uma nota de F a A+. Com os passos anteriores aplicados, deve obter A+.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Visite o SSL Labs e introduza o seu dom\u00ednio, ou use a linha de comandos para uma verifica\u00e7\u00e3o r\u00e1pida com OpenSSL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>openssl s_client -connect exemplo.pt:443 -tls1_3 &lt;\/dev\/null 2&gt;\/dev\/null | grep -E \"Protocol|Cipher\"\n# Protocol  : TLSv1.3\n# Cipher    : TLS_AES_256_GCM_SHA384\n\ncurl -sI https:\/\/exemplo.pt | grep -i strict-transport\n# strict-transport-security: max-age=63072000; includeSubDomains; preload<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Os pontos que o SSL Labs verifica e que esta configura\u00e7\u00e3o j\u00e1 cobre: protocolos modernos (sem TLS 1.0\/1.1), cifras com confidencialidade futura (forward secrecy via ECDHE), HSTS ativo, e cadeia de certificados completa. Se receber menos do que A, o relat\u00f3rio indica exatamente o que falta, normalmente um protocolo antigo ainda ativo ou um cabe\u00e7alho HSTS em falta.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-11-limitar-pedidos-rate-limiting\">Passo 11: Limitar pedidos (rate limiting)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O TLS protege o tr\u00e1fego, mas n\u00e3o trava ataques de for\u00e7a bruta nem inunda\u00e7\u00f5es de pedidos. O Nginx tem rate limiting nativo atrav\u00e9s do m\u00f3dulo <code>limit_req<\/code>. Os ataques automatizados contra p\u00e1ginas de in\u00edcio de sess\u00e3o e contra a interface de administra\u00e7\u00e3o s\u00e3o uma constante; limitar a taxa de pedidos reduz drasticamente a superf\u00edcie de abuso.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Defina uma zona de limite no bloco <code>http<\/code> (em <code>\/etc\/nginx\/nginx.conf<\/code>) e aplique-a \u00e0s rotas sens\u00edveis:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Dentro do bloco http { ... }\nlimit_req_zone $binary_remote_addr zone=login:10m rate=5r\/s;\n\n# Dentro do server { ... } da porta 443\nlocation \/wp-login.php {\n    limit_req zone=login burst=10 nodelay;\n    try_files $uri $uri\/ =404;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A configura\u00e7\u00e3o acima permite 5 pedidos por segundo por endere\u00e7o IP, com uma rajada (burst) de 10 antes de come\u00e7ar a devolver erros 503. A zona de 10 MB guarda o estado de cerca de 160 000 endere\u00e7os. Para um endurecimento mais forte contra for\u00e7a bruta na camada de rede, combine isto com o <code>fail2ban<\/code>, que bane IPs ap\u00f3s v\u00e1rias tentativas falhadas (com valores predefinidos t\u00edpicos de 5 tentativas, janela de 10 minutos e banimento de 10 minutos).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00e3o exagere nos limites: valores demasiado baixos bloqueiam utilizadores leg\u00edtimos atr\u00e1s de NAT corporativo, onde muitas pessoas partilham um IP. Comece conservador, observe os registos em <code>\/var\/log\/nginx\/error.log<\/code> e ajuste conforme o tr\u00e1fego real.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"passo-12-o-ficheiro-de-configuracao-completo\">Passo 12: O ficheiro de configura\u00e7\u00e3o completo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eis a configura\u00e7\u00e3o final, juntando todos os passos. Use-a como ponto de partida para o seu site, substituindo <code>exemplo.pt<\/code> pelo seu dom\u00ednio. Este \u00e9 o projeto completo e funcional do tutorial.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/nginx\/sites-available\/exemplo.pt\n\n# Bloco HTTP: desafio ACME + redirecionamento 301\nserver {\n    listen 80;\n    listen [::]:80;\n    server_name exemplo.pt www.exemplo.pt;\n\n    location \/.well-known\/acme-challenge\/ {\n        root \/var\/www\/exemplo.pt\/html;\n    }\n    location \/ {\n        return 301 https:\/\/$host$request_uri;\n    }\n}\n\n# Bloco HTTPS endurecido\nserver {\n    listen 443 ssl;\n    listen [::]:443 ssl;\n    http2 on;\n    server_name exemplo.pt www.exemplo.pt;\n\n    root \/var\/www\/exemplo.pt\/html;\n    index index.html;\n\n    # Certificados Let's Encrypt\n    ssl_certificate     \/etc\/letsencrypt\/live\/exemplo.pt\/fullchain.pem;\n    ssl_certificate_key \/etc\/letsencrypt\/live\/exemplo.pt\/privkey.pem;\n\n    # Protocolos e cifras (perfil Mozilla intermediate)\n    ssl_protocols TLSv1.2 TLSv1.3;\n    ssl_prefer_server_ciphers off;\n    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;\n\n    ssl_session_timeout 1d;\n    ssl_session_cache shared:MozSSL:10m;\n    ssl_session_tickets off;\n\n    # Cabe\u00e7alhos de seguran\u00e7a\n    add_header Strict-Transport-Security \"max-age=63072000; includeSubDomains; preload\" always;\n    add_header X-Content-Type-Options \"nosniff\" always;\n    add_header X-Frame-Options \"SAMEORIGIN\" always;\n    add_header Referrer-Policy \"strict-origin-when-cross-origin\" always;\n    add_header Permissions-Policy \"geolocation=(), microphone=(), camera=()\" always;\n\n    location \/ {\n        try_files $uri $uri\/ =404;\n    }\n\n    location \/wp-login.php {\n        limit_req zone=login burst=10 nodelay;\n        try_files $uri $uri\/ =404;\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Depois de gravar, valide e recarregue uma \u00faltima vez. Se o <code>nginx -t<\/code> reportar sucesso, tem uma configura\u00e7\u00e3o de produ\u00e7\u00e3o pronta a servir tr\u00e1fego cifrado com nota A+.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo nginx -t &amp;&amp; sudo systemctl reload nginx\n# nginx: configuration file \/etc\/nginx\/nginx.conf test is successful<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"dicas-avancadas-troca-de-chaves-pos-quantica\">Dicas avan\u00e7adas: troca de chaves p\u00f3s-qu\u00e2ntica<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A amea\u00e7a dos computadores qu\u00e2nticos ao TLS \u00e9 real a m\u00e9dio prazo, com o cen\u00e1rio &#8220;colher agora, decifrar depois&#8221; (harvest now, decrypt later) a preocupar quem protege dados de longa dura\u00e7\u00e3o. A resposta da ind\u00fastria j\u00e1 chegou ao TLS 1.3: a troca de chaves h\u00edbrida p\u00f3s-qu\u00e2ntica. O Chrome e o Firefox j\u00e1 usam por predefini\u00e7\u00e3o o esquema X25519MLKEM768, que combina a curva el\u00edptica cl\u00e1ssica X25519 com o algoritmo ML-KEM (Kyber), padronizado pelo NIST.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Se o seu Nginx estiver compilado com OpenSSL 3.5 ou superior (ou com uma vers\u00e3o recente da BoringSSL), pode ativar este grupo no servidor para que o aperto de m\u00e3o fique resistente a ataques qu\u00e2nticos. A diretiva \u00e9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    ssl_ecdh_curve X25519MLKEM768:X25519:secp256r1;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Quando o cliente e o servidor suportam X25519MLKEM768, o segredo partilhado fica protegido contra um futuro advers\u00e1rio qu\u00e2ntico, sem perder a seguran\u00e7a cl\u00e1ssica do X25519. Esta \u00e9 a forma mais simples de come\u00e7ar a transi\u00e7\u00e3o p\u00f3s-qu\u00e2ntica no seu site hoje. Verifique a vers\u00e3o do OpenSSL antes: se a sua compila\u00e7\u00e3o n\u00e3o suportar o grupo, o Nginx falha a recarregar e basta remover a diretiva.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Outras otimiza\u00e7\u00f5es que vale a pena considerar: ativar o HTTP\/2 (j\u00e1 inclu\u00eddo no projeto completo com <code>http2 on<\/code>) para multiplexar pedidos numa s\u00f3 liga\u00e7\u00e3o, ou experimentar o HTTP\/3 sobre QUIC se a sua vers\u00e3o de Nginx o suportar. O HTTP\/3 reduz ainda mais a lat\u00eancia ao eliminar o bloqueio em cabe\u00e7a de linha do TCP, embora exija abrir a porta 443 tamb\u00e9m em UDP na firewall.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"lets-encrypt-vale-a-pena-face-as-autoridades-pagas\">Let&#8217;s Encrypt vale a pena face \u00e0s autoridades pagas?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uma d\u00favida comum de quem configura HTTPS pela primeira vez: se a Let&#8217;s Encrypt \u00e9 gratuita, qual o sentido de pagar dezenas ou centenas de euros por ano a uma autoridade comercial? A resposta curta \u00e9 que, para a maioria dos casos, n\u00e3o h\u00e1 sentido nenhum. A criptografia \u00e9 id\u00eantica e o cadeado no navegador \u00e9 exatamente o mesmo. As diferen\u00e7as est\u00e3o na valida\u00e7\u00e3o, no suporte e nalgumas funcionalidades de nicho.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Crit\u00e9rio<\/th><th>Let&#8217;s Encrypt<\/th><th>Autoridade paga (DV)<\/th><th>Autoridade paga (OV\/EV)<\/th><\/tr><\/thead><tbody><tr><td>Pre\u00e7o anual<\/td><td>Gratuito<\/td><td>10 a 60 euros<\/td><td>100 a 400+ euros<\/td><\/tr><tr><td>Validade<\/td><td>90 dias (auto)<\/td><td>at\u00e9 1 ano<\/td><td>at\u00e9 1 ano<\/td><\/tr><tr><td>Tipo de valida\u00e7\u00e3o<\/td><td>Dom\u00ednio (DV)<\/td><td>Dom\u00ednio (DV)<\/td><td>Organiza\u00e7\u00e3o \/ estendida<\/td><\/tr><tr><td>Wildcard<\/td><td>Sim (DNS-01)<\/td><td>Sim<\/td><td>Sim<\/td><\/tr><tr><td>Automatiza\u00e7\u00e3o ACME<\/td><td>Total<\/td><td>Vari\u00e1vel<\/td><td>Limitada<\/td><\/tr><tr><td>Garantia \/ seguro<\/td><td>N\u00e3o<\/td><td>Sim<\/td><td>Sim (valores altos)<\/td><\/tr><tr><td>Suporte dedicado<\/td><td>Comunidade<\/td><td>Sim<\/td><td>Sim<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A valida\u00e7\u00e3o estendida (EV), que outrora mostrava o nome da empresa em verde na barra de endere\u00e7o, perdeu praticamente todo o valor: os navegadores deixaram de a destacar visualmente h\u00e1 v\u00e1rios anos. Para um blogue, uma loja online ou uma API, a Let&#8217;s Encrypt cobre tudo o que precisa. As autoridades pagas continuam a fazer sentido sobretudo quando precisa de um seguro contratual associado ao certificado, ou de valida\u00e7\u00e3o OV para cumprir requisitos espec\u00edficos de um cliente empresarial.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">H\u00e1 ainda alternativas gratuitas \u00e0 Let&#8217;s Encrypt que tamb\u00e9m falam ACME, como a ZeroSSL e a autoridade da Google. O fluxo com o certbot \u00e9 semelhante, bastando apontar para um servidor ACME diferente. Ter mais do que uma op\u00e7\u00e3o de autoridade gratuita \u00e9 saud\u00e1vel para a resili\u00eancia da web, mas para come\u00e7ar a Let&#8217;s Encrypt continua a ser a escolha mais simples e mais documentada.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"erros-comuns-e-armadilhas-a-evitar\">Erros comuns e armadilhas a evitar<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Mesmo um processo simples tem pontos onde \u00e9 f\u00e1cil trope\u00e7ar. Estas s\u00e3o as armadilhas mais frequentes ao configurar HTTPS no <strong>Nginx<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>DNS n\u00e3o propagado:<\/strong> pedir o certificado antes de o registo A apontar para o servidor causa a falha &#8220;Timeout during connect&#8221; no desafio HTTP-01. Espere a propaga\u00e7\u00e3o e confirme com <code>dig<\/code>.<\/li><li><strong>Esquecer a porta 443 na firewall:<\/strong> o site funciona em HTTP mas o HTTPS d\u00e1 timeout. Verifique o <code>ufw<\/code> e o grupo de seguran\u00e7a da nuvem.<\/li><li><strong>Recarregar sem testar:<\/strong> editar a configura\u00e7\u00e3o e fazer <code>reload<\/code> sem <code>nginx -t<\/code> pode derrubar todos os sites do servidor por um erro de sintaxe.<\/li><li><strong>HSTS preload prematuro:<\/strong> ativar <code>preload<\/code> antes de garantir HTTPS em todos os subdom\u00ednios bloqueia o acesso e \u00e9 dif\u00edcil de reverter.<\/li><li><strong>Atingir os limites da Let&#8217;s Encrypt:<\/strong> repetir emiss\u00f5es em testes esgota o limite de 5 certificados por dom\u00ednio por semana. Use sempre <code>--dry-run<\/code> nos testes.<\/li><li><strong>Chave privada exposta:<\/strong> versionar <code>privkey.pem<\/code> num reposit\u00f3rio ou dar-lhe permiss\u00f5es abertas anula toda a seguran\u00e7a. Mantenha as permiss\u00f5es restritas a root.<\/li><li><strong>Cabe\u00e7alhos sem <code>always<\/code>:<\/strong> sem essa palavra-chave, os cabe\u00e7alhos de seguran\u00e7a desaparecem nas respostas de erro.<\/li><li><strong>Misturar conte\u00fado HTTP em p\u00e1gina HTTPS:<\/strong> imagens ou scripts carregados por <code>http:\/\/<\/code> geram avisos de &#8220;conte\u00fado misto&#8221; e quebram o cadeado.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"resolucao-de-problemas-troubleshooting\">Resolu\u00e7\u00e3o de problemas (troubleshooting)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quando algo corre mal, o registo de erros do Nginx em <code>\/var\/log\/nginx\/error.log<\/code> e os registos do certbot em <code>\/var\/log\/letsencrypt\/letsencrypt.log<\/code> s\u00e3o o primeiro s\u00edtio a consultar. Eis os problemas mais comuns e como os resolver.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Sintoma<\/th><th>Causa prov\u00e1vel<\/th><th>Solu\u00e7\u00e3o<\/th><\/tr><\/thead><tbody><tr><td>&#8220;Timeout during connect&#8221; na emiss\u00e3o<\/td><td>Porta 80 fechada ou DNS errado<\/td><td>Abrir porta 80, confirmar registo A com dig<\/td><\/tr><tr><td>&#8220;Connection refused&#8221; em HTTPS<\/td><td>Porta 443 fechada<\/td><td>Abrir &#8216;Nginx Full&#8217; no ufw e na nuvem<\/td><\/tr><tr><td>NET::ERR_CERT_COMMON_NAME_INVALID<\/td><td>Certificado n\u00e3o cobre o dom\u00ednio usado<\/td><td>Reemitir com todos os -d necess\u00e1rios<\/td><\/tr><tr><td>Aviso &#8220;conte\u00fado misto&#8221;<\/td><td>Recursos carregados por HTTP<\/td><td>Mudar URLs internos para https:\/\/ ou \/\/<\/td><\/tr><tr><td>&#8220;too many certificates already issued&#8221;<\/td><td>Limite semanal da Let&#8217;s Encrypt atingido<\/td><td>Esperar uma semana ou usar staging<\/td><\/tr><tr><td>Cadeado parcial \/ nota B no SSL Labs<\/td><td>TLS 1.0\/1.1 ainda ativo<\/td><td>Definir ssl_protocols TLSv1.2 TLSv1.3<\/td><\/tr><tr><td>nginx: [emerg] cannot load certificate<\/td><td>Caminho do certificado errado<\/td><td>Conferir caminhos em \/etc\/letsencrypt\/live\/<\/td><\/tr><tr><td>Renova\u00e7\u00e3o autom\u00e1tica falha<\/td><td>Bloco da porta 80 removido<\/td><td>Restaurar location do acme-challenge<\/td><\/tr><tr><td>HSTS n\u00e3o aparece em respostas 404<\/td><td>Falta a palavra-chave always<\/td><td>Adicionar always a cada add_header<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Para depurar a emiss\u00e3o sem gastar limites reais, use o ambiente de testes (staging) da Let&#8217;s Encrypt com a flag <code>--staging<\/code>. Os certificados de staging n\u00e3o s\u00e3o confi\u00e1veis pelos navegadores, mas validam todo o fluxo. Quando tudo funcionar, repita sem a flag para obter o certificado de produ\u00e7\u00e3o. Se mudou de servidor e os certificados antigos ficaram para tr\u00e1s, pode for\u00e7ar a reemiss\u00e3o com <code>--force-renewal<\/code>, mas use-o com parcim\u00f3nia por causa dos limites.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Um erro subtil: depois de obter A+ no SSL Labs, alguns administradores notam que o site fica lento no primeiro acesso. A causa costuma ser a falta de cache de sess\u00e3o TLS bem dimensionada. A diretiva <code>ssl_session_cache shared:MozSSL:10m<\/code> resolve, permitindo retomar sess\u00f5es sem repetir o aperto de m\u00e3o completo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"perguntas-frequentes\">Perguntas frequentes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"os-certificados-da-lets-encrypt-sao-mesmo-gratuitos-e-seguros\">Os certificados da Let&#8217;s Encrypt s\u00e3o mesmo gratuitos e seguros?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sim. S\u00e3o totalmente gratuitos e usam a mesma criptografia dos certificados pagos. A diferen\u00e7a est\u00e1 na valida\u00e7\u00e3o: a Let&#8217;s Encrypt s\u00f3 faz valida\u00e7\u00e3o de dom\u00ednio (DV), n\u00e3o de organiza\u00e7\u00e3o (OV) nem estendida (EV). Para a esmagadora maioria dos sites, a valida\u00e7\u00e3o de dom\u00ednio \u00e9 suficiente e os navegadores tratam o cadeado exatamente da mesma forma.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"com-que-frequencia-preciso-de-renovar-o-certificado\">Com que frequ\u00eancia preciso de renovar o certificado?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Os certificados duram 90 dias, mas com a renova\u00e7\u00e3o autom\u00e1tica via certbot n\u00e3o precisa de fazer nada manualmente. O temporizador do systemd verifica duas vezes por dia e renova quando faltam 30 dias ou menos. Fa\u00e7a um <code>certbot renew --dry-run<\/code> de vez em quando para confirmar que o automatismo continua saud\u00e1vel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"devo-usar-tls-1-2-ou-tls-1-3\">Devo usar TLS 1.2 ou TLS 1.3?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ative os dois. O TLS 1.3 \u00e9 mais r\u00e1pido e mais seguro e deve ser a prefer\u00eancia, mas alguns clientes mais antigos ainda s\u00f3 falam TLS 1.2. Manter TLS 1.2 como m\u00ednimo garante compatibilidade sem comprometer a seguran\u00e7a, desde que use cifras AEAD com ECDHE. Desative tudo abaixo disso.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"o-hsts-pode-trancar-me-fora-do-meu-site\">O HSTS pode trancar-me fora do meu site?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">O HSTS normal n\u00e3o, porque pode sempre desativ\u00e1-lo e esperar que o <code>max-age<\/code> expire. O perigo est\u00e1 no <code>preload<\/code>: uma vez na lista dos navegadores, voltar a servir HTTP torna-se imposs\u00edvel durante semanas. S\u00f3 ative o preload quando tiver a certeza absoluta de que todo o dom\u00ednio e subdom\u00ednios usar\u00e3o HTTPS para sempre.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"preciso-de-chaves-diffie-hellman-personalizadas\">Preciso de chaves Diffie-Hellman personalizadas?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">J\u00e1 n\u00e3o. As cifras modernas recomendadas usam ECDHE (troca de chaves de curva el\u00edptica), que n\u00e3o depende dos par\u00e2metros DH cl\u00e1ssicos. A antiga pr\u00e1tica de gerar um ficheiro <code>dhparam.pem<\/code> de 2048 ou 4096 bits deixou de ser necess\u00e1ria para a configura\u00e7\u00e3o de refer\u00eancia intermediate da Mozilla em 2026.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"como-ativo-https-para-um-certificado-wildcard\">Como ativo HTTPS para um certificado wildcard?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Os certificados wildcard (*.exemplo.pt) exigem o desafio DNS-01 em vez do HTTP-01, porque a Let&#8217;s Encrypt precisa de validar o controlo do DNS. Use <code>certbot certonly --manual --preferred-challenges dns -d \"*.exemplo.pt\"<\/code> ou, melhor, um plugin de DNS do seu fornecedor para automatizar a renova\u00e7\u00e3o.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"o-nginx-ou-o-apache-e-melhor-para-terminacao-tls\">O Nginx ou o Apache \u00e9 melhor para termina\u00e7\u00e3o TLS?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ambos suportam TLS 1.3 e Let&#8217;s Encrypt. O Nginx tende a ter melhor desempenho com muitas liga\u00e7\u00f5es simult\u00e2neas e um modelo de configura\u00e7\u00e3o mais limpo para reverse proxy. Para sites de alto tr\u00e1fego e para servir conte\u00fado est\u00e1tico, o Nginx \u00e9 normalmente a escolha mais eficiente.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusao-um-nginx-seguro-em-30-minutos\">Conclus\u00e3o: um Nginx seguro em 30 minutos<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Em 12 passos passou de um servidor sem cifra para uma configura\u00e7\u00e3o de produ\u00e7\u00e3o com TLS 1.3, certificados gratuitos e renov\u00e1veis da Let&#8217;s Encrypt, HSTS, cabe\u00e7alhos de seguran\u00e7a, rate limiting e at\u00e9 troca de chaves p\u00f3s-qu\u00e2ntica. A nota A+ no SSL Labs deixa de ser um luxo e passa a ser o ponto de partida.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O segredo de uma configura\u00e7\u00e3o TLS que dura \u00e9 a automatiza\u00e7\u00e3o. Com a renova\u00e7\u00e3o tratada pelo certbot e uma monitoriza\u00e7\u00e3o externa para o avisar, o site mant\u00e9m-se seguro sem trabalho manual. Reavalie a configura\u00e7\u00e3o de cifras uma ou duas vezes por ano, acompanhando a evolu\u00e7\u00e3o do perfil intermediate da Mozilla, e estar\u00e1 sempre alinhado com as melhores pr\u00e1ticas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Se gere v\u00e1rios servidores, considere transformar este ficheiro de configura\u00e7\u00e3o num modelo reutiliz\u00e1vel, por exemplo atrav\u00e9s de um snippet inclu\u00eddo com a diretiva <code>include<\/code> do Nginx. Assim, ajustes futuros \u00e0s cifras ou aos cabe\u00e7alhos de seguran\u00e7a aplicam-se a todos os sites num \u00fanico s\u00edtio, sem ter de editar cada server block. A combina\u00e7\u00e3o de TLS 1.3, Let&#8217;s Encrypt e <strong>Nginx<\/strong> \u00e9 hoje a base padr\u00e3o da web segura, e domina-la d\u00e1-lhe uma vantagem pr\u00e1tica que se aplica a qualquer projeto, do blogue pessoal \u00e0 API de produ\u00e7\u00e3o.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"cobertura-relacionada\">Cobertura relacionada<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"\/pt\/https-e-tls\/\">HTTPS e TLS: O que o Cadeado Protege (e o que N\u00e3o Protege)<\/a><\/li><li><a href=\"\/pt\/autenticacao-ssh-chaves\/\">Autentica\u00e7\u00e3o SSH com Chaves Ed25519: 12 Passos<\/a><\/li><li><a href=\"\/pt\/mtls-node-js-tls-13\/\">mTLS em Node.js: TLS 1.3 em 12 Passos<\/a><\/li><li><a href=\"\/pt\/wireshark-analise-pacotes-12-passos\/\">Wireshark: An\u00e1lise de Pacotes em 12 Passos<\/a><\/li><li><a href=\"\/pt\/ciberataques-portugal-2026\/\">Ciberataques em Portugal: 2.437\/Semana<\/a><\/li><li><a href=\"\/pt\/security-hub\/\">Seguran\u00e7a Online Explicada: Guia Completo<\/a><\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"fontes-e-leitura-adicional\">Fontes e leitura adicional<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/letsencrypt.org\/getting-started\/\" target=\"_blank\" rel=\"noopener\">Let&#8217;s Encrypt: Getting Started<\/a><\/li><li><a href=\"https:\/\/eff-certbot.readthedocs.io\/en\/stable\/\" target=\"_blank\" rel=\"noopener\">Documenta\u00e7\u00e3o oficial do Certbot (EFF)<\/a><\/li><li><a href=\"https:\/\/ssl-config.mozilla.org\/\" target=\"_blank\" rel=\"noopener\">Mozilla SSL Configuration Generator<\/a><\/li><li><a href=\"https:\/\/nginx.org\/en\/docs\/http\/configuring_https_servers.html\" target=\"_blank\" rel=\"noopener\">Nginx: Configuring HTTPS servers<\/a><\/li><li><a href=\"https:\/\/www.ssllabs.com\/ssltest\/\" target=\"_blank\" rel=\"noopener\">Qualys SSL Labs Server Test<\/a><\/li><li><a href=\"https:\/\/datatracker.ietf.org\/doc\/html\/rfc8446\" target=\"_blank\" rel=\"noopener\">RFC 8446: TLS 1.3<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Servir um site sobre HTTP simples deixou de ser uma op\u00e7\u00e3o em 2026. Os navegadores marcam qualquer p\u00e1gina sem cadeado como &#8220;N\u00e3o seguro&#8221;, o Google penaliza no ranking e qualquer\u2026<\/p>\n","protected":false},"author":5,"featured_media":101,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-100","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-security"],"_links":{"self":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts\/100","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/comments?post=100"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts\/100\/revisions"}],"predecessor-version":[{"id":102,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/posts\/100\/revisions\/102"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/media\/101"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/media?parent=100"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/categories?post=100"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/pt\/wp-json\/wp\/v2\/tags?post=100"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}