Como criei este blog sem gastar um centavo (e sem tocar em WordPress)

Neste post
Por que abandonar o WordPress#
O peso de manter um CMS dinâmico#
O WordPress é um software extraordinário que alimenta quase metade da internet. Dito isso, manter uma instalação WordPress saudável é um trabalho que nunca termina. Cada visita ao seu site dispara uma cadeia de eventos: o servidor recebe a requisição, o PHP acorda, consulta o MySQL, monta a página na hora, e devolve o HTML para o navegador. Multiplica isso por cem visitantes simultâneos e você tem um servidor suando para entregar páginas que, na maioria dos blogs, são exatamente iguais para todo mundo.
E aí vem o resto. Plugins precisam de atualização constante — não por capricho dos desenvolvedores, mas porque cada plugin é uma porta de entrada potencial para quem quer invadir o seu site. O próprio WordPress exige atualizações regulares pelo mesmo motivo. O banco de dados cresce, acumula revisões de posts, transients expirados, e metadados de plugins que você desinstalou há dois anos. O servidor precisa de PHP na versão certa, extensões habilitadas, e memória suficiente para não cair quando o Google resolver indexar tudo de uma vez. Quem administra um WordPress sabe que a pergunta não é se vai ter problema, é quando.
Some a isto a voracidade dos scrapers de conteúdo, tanto os usados para treinar IAs quanto os usados pela concorrência (ou por você mesmo) para analisar conteúdo, SEO, etc.
Nada disso é culpa do WordPress em si. Ele foi feito para ser flexível, e flexibilidade tem um custo. O problema aparece quando tudo o que você quer é publicar texto e imagens — e o sistema que deveria te ajudar nisso exige mais manutenção do que o conteúdo em si.
O que um site estático resolve (e o que não resolve)#
Um site estático é o oposto dessa engrenagem. Em vez de montar cada página na hora da visita, todas as páginas são geradas de antemão — arquivos HTML puros que ficam prontos no servidor esperando alguém pedir. Não tem PHP rodando, não tem banco de dados, não tem login de administrador exposto na internet. O servidor só precisa fazer uma coisa: entregar arquivos. Qualquer servidor faz isso rápido, e o resultado é um site que carrega em milissegundos e que não tem superfície de ataque para explorar.
A segurança melhora drasticamente. Sem banco de dados, não existe SQL injection. Sem painel de admin acessível pela web, não tem brute force em /wp-login.php. Sem PHP, não tem execução remota de código. O site é um conjunto de arquivos de texto — tão vulnerável quanto uma pasta de documentos num servidor de arquivos.
Mas convém ser honesto sobre os limites. Um site estático não é um substituto direto do WordPress para todo mundo. Funcionalidades que dependem de processamento no servidor — comentários nativos (embora existam soluções self-hosted para isso), busca no banco de dados, e-commerce com carrinho de compras, área de membros com login — não existem num HTML puro. Elas podem ser adicionadas com serviços externos (e vamos falar sobre isso mais adiante), mas a complexidade aumenta. Se o seu site depende fortemente de interação em tempo real ou de funcionalidades dinâmicas, o WordPress ainda faz sentido. Para blogs, sites de notícias, portfólios, documentação e sites institucionais, um gerador estático entrega mais resultado com menos dor de cabeça.
As peças do quebra-cabeça#
Para um site estático funcionar como um blog de verdade — com editor visual, deploy automático e sem depender da sua máquina — você precisa de quatro peças trabalhando juntas. Nenhuma delas é complicada sozinha, mas entender o papel de cada uma antes de começar evita confusão depois.
Hugo — o gerador de sites#
O Hugo é o programa que transforma os seus textos em um site HTML completo. Você escreve em Markdown (um formato de texto simples com marcações leves para títulos, links, negrito e afins), escolhe um tema visual, e o Hugo compila tudo em páginas HTML prontas para publicar. Ele gera o site inteiro de uma vez — todos os posts, todas as páginas de categoria, o feed RSS, o sitemap — e coloca o resultado numa pasta chamada public/.
O Hugo é escrito em Go e é absurdamente rápido. Um site com 500 posts compila em menos de um segundo. Isso importa porque toda vez que alguém publica um post novo, o site inteiro precisa ser reconstruído, e ninguém quer esperar minutos por isso.
Existem outros geradores estáticos — Jekyll, Eleventy, Astro — mas o Hugo tem a combinação certa de velocidade, maturidade e ecossistema de temas para o que estamos montando aqui.
Pages CMS — o editor para humanos#
Um gerador estático, sozinho, exige que você edite arquivos de texto num repositório Git. Para um desenvolvedor, isso é natural. Para qualquer outra pessoa, é um pesadelo. O Pages CMS resolve esse problema colocando uma interface web amigável na frente do repositório.
Na prática, o autor abre o Pages CMS no navegador, vê uma lista de posts, clica em “novo post”, preenche os campos (título, data, categorias, conteúdo), e salva. Nos bastidores, o Pages CMS pega tudo isso e cria um arquivo Markdown no repositório do GitHub com o front matter (os metadados do post) e o corpo do texto formatado. O autor não precisa saber que o Git existe.
O Pages CMS é open source, gratuito, e roda no próprio serviço deles — você não instala nada no seu servidor. A configuração inteira é um único arquivo YAML no repositório que define quais campos cada tipo de conteúdo tem.
Cloudflare — a hospedagem invisível#
O Cloudflare entra como a infraestrutura que constrói e hospeda o site. Quando alguém publica um post pelo Pages CMS (ou quando você faz um push direto no repositório), o Cloudflare detecta a mudança, clona o repositório, roda o Hugo para gerar o HTML, e distribui o resultado pela sua rede global de mais de 300 data centers. O visitante em São Paulo recebe o site de um servidor em São Paulo; o visitante em Lisboa recebe de um servidor em Lisboa.
O plano gratuito do Cloudflare oferece bandwidth ilimitado e 500 builds por mês — o suficiente para publicar mais de 15 posts por dia sem pagar nada. O site fica rápido por padrão, HTTPS é automático, e se uma matéria viralizar, o Cloudflare absorve o tráfego sem piscar.
GitHub — o cofre onde tudo mora#
O GitHub é o repositório central. Todo o conteúdo do site — os textos em Markdown, as imagens, a configuração do Hugo, os arquivos do tema — vive num repositório Git. Isso significa que cada alteração é versionada: se alguém editar um post e o resultado ficar ruim, basta voltar para a versão anterior. Se alguém deletar algo por engano, o histórico completo está preservado.
O GitHub também é o ponto de conexão entre as outras peças. O Pages CMS se comunica com o GitHub para ler e gravar conteúdo. O Cloudflare se conecta ao GitHub para detectar mudanças e disparar o build. O seu computador, se você preferir editar localmente, também fala com o GitHub via Git. Tudo converge para o mesmo lugar, e tudo fica registrado.
Como as peças se encaixam#
Quatro ferramentas independentes não servem de nada se não conversam entre si. A boa notícia é que a integração entre elas é quase automática — uma vez configurada, o fluxo funciona sem intervenção humana.
O fluxo: do editor ao site publicado#
Tudo começa no Pages CMS. O autor abre o navegador, acessa o painel, e escreve um post. Pode estar no computador do escritório, no laptop do sofá, ou no celular no ônibus — tanto faz, é uma página web. Ele preenche o título, escolhe uma categoria, escreve o texto, sobe uma imagem, e clica em salvar.
O Pages CMS pega esses dados e faz um commit no repositório do GitHub. Na prática, ele cria um arquivo Markdown com os metadados do post no cabeçalho (título, data, categorias) e o conteúdo logo abaixo. Se o autor subiu uma imagem, ela vai junto para a pasta de mídia do repositório. Tudo isso acontece via API do GitHub — o Pages CMS nunca armazena nada, ele apenas lê e escreve no repositório.
O GitHub recebe esse commit e dispara um webhook para o Cloudflare. O Cloudflare acorda, clona a versão mais recente do repositório, e executa o Hugo. O Hugo lê todos os arquivos Markdown, aplica o tema visual, e gera o site inteiro — cada post vira uma página HTML, as listagens são recriadas, o feed RSS é atualizado, o sitemap é refeito. O resultado é uma pasta com dezenas (ou centenas) de arquivos HTML estáticos, prontos para servir.
O Cloudflare distribui esses arquivos pela sua rede de data centers espalhados pelo mundo. Quando alguém acessa o site, recebe o HTML do servidor mais próximo. Não tem fila, não tem processamento, não tem banco de dados — só um arquivo sendo entregue.
Do clique em “salvar” até o post estar no ar, o processo inteiro leva entre um e dois minutos. A maior parte desse tempo é o Cloudflare preparando o ambiente de build. O Hugo em si compila o site em menos de um segundo.
O que acontece quando alguém clica em “Salvar”#
Vale detalhar a sequência porque ela mostra onde cada peça atua e onde as coisas podem dar errado:
O autor clica em “Salvar” no Pages CMS. O CMS envia uma requisição para a API do GitHub com o conteúdo do arquivo Markdown e, se houver imagem, o binário da imagem. O GitHub recebe e registra como um commit normal — com autor, data, e mensagem. Esse commit aparece no histórico do repositório como qualquer outro, e pode ser revertido se necessário.
O GitHub notifica o Cloudflare de que houve uma mudança no branch principal. O Cloudflare inicia o processo de build: provisiona um container temporário, clona o repositório, instala a versão do Hugo que você configurou, e executa o comando de build. Se o build falhar — um erro de sintaxe no template, um arquivo corrompido — o Cloudflare mantém a versão anterior do site no ar e registra o erro no log. O site nunca fica fora do ar por causa de um build quebrado.
Se o build passar, o Cloudflare compara os arquivos gerados com os que já estão distribuídos na rede. Só os arquivos que mudaram são atualizados — se você publicou um post novo, apenas as páginas afetadas (o post, a listagem, o RSS) são redistribuídas. O restante do site continua como estava, servido do cache.
O resultado final é que o autor escreve num editor web amigável e, dois minutos depois, o conteúdo está disponível para o mundo inteiro num site que carrega em milissegundos. Sem servidor para manter, sem banco de dados para otimizar, sem medo de ataque. Se o autor publicar algo errado, basta editar ou reverter — o histórico completo está no GitHub.
Mãos à obra#
A partir daqui, cada passo tem comandos reais que você vai executar no terminal. Se algo der errado, pare e leia a mensagem de erro antes de continuar — a maioria dos problemas nessa fase são erros de digitação ou de caminho.
Pré-requisitos#
Você vai precisar de quatro coisas instaladas na sua máquina antes de começar:
Git — o sistema de controle de versão. Se você usa Mac, provavelmente já tem (digite git --version no terminal para conferir). No Linux, sudo apt install git resolve. No Windows, baixe em git-scm.com.
Hugo — o gerador de sites. No Mac com Homebrew: brew install hugo. No Linux, baixe o .deb da página de releases no GitHub do Hugo. No Windows, Chocolatey ou Scoop instalam com um comando. Confirme com hugo version — você precisa da versão 0.128 ou superior, e do Hugo extended (que é o padrão nas instalações via gerenciador de pacotes).
Uma conta no GitHub — gratuita, em github.com. Se você trabalha com desenvolvimento, já tem. Se não tem, crie uma agora.
Uma conta no Cloudflare — gratuita, em dash.cloudflare.com. Você vai usar o plano free, que é mais do que suficiente para o que estamos fazendo.
Não precisa instalar mais nada. O Pages CMS roda no navegador e não exige instalação local.
Criando o repositório no GitHub#
Acesse github.com/new e crie um novo repositório:
- Repository name: escolha algo curto e sem espaços, como
meu-blogou o slug do seu domínio. - Visibility: Private (seu código não precisa ser público).
- Initialize with: marque “Add a README file”.
Depois de criar, clone o repositório na sua máquina. Abra o terminal e digite:
cd ~/projects
git clone git@github.com:SEU-USUARIO/meu-blog.git
cd meu-blog
Substitua SEU-USUARIO pelo seu nome de usuário no GitHub e meu-blog pelo nome que escolheu. Se a pasta ~/projects não existir, crie com mkdir ~/projects antes.
Instalando o Hugo e escolhendo um tema#
Com o terminal aberto dentro do diretório do seu repositório, crie a estrutura do site Hugo:
hugo new site . --force
O --force é necessário porque o diretório já existe (tem o README do GitHub). O Hugo vai criar várias pastas — content/, layouts/, static/, themes/ — e um arquivo hugo.toml com configuração mínima.
Agora adicione o tema. No nosso caso usamos o Terminal, um tema com estética de linha de comando que combina com um blog de tecnologia:
git submodule add https://github.com/panr/hugo-theme-terminal.git themes/terminal
O comando git submodule add baixa o tema e o registra como dependência do seu repositório. Isso é importante: quando o Cloudflare clonar o repo para fazer o build, ele vai saber que precisa baixar o tema também.
Escolher um tema é uma decisão que você pode mudar depois, mas que é mais fácil acertar de primeira. O diretório de temas do Hugo em themes.gohugo.io tem centenas de opções. Para um blog de notícias ou magazine, o Mainroad é uma boa pedida. Para documentação técnica, o Docsy. Para algo minimalista, o PaperMod. Navegue, veja as demos, e escolha um que tenha a cara do que você quer — a instalação é sempre o mesmo comando git submodule add com a URL do tema.
Configurando o site#
Apague o hugo.toml gerado automaticamente e crie um novo com a configuração do seu site. Cada tema tem suas próprias opções, mas a estrutura básica é parecida. Aqui está um exemplo funcional para o tema Terminal:
baseURL = "https://seu-dominio.com/"
title = "Nome do seu blog"
languageCode = "pt-BR"
defaultContentLanguage = "pt"
theme = "terminal"
paginate = 5
[params]
contentTypeName = "posts"
themeColor = "orange"
showMenuItems = 3
fullWidthTheme = false
centerTheme = true
subtitle = "Sua tagline aqui"
[params.logo]
logoText = "Nome do seu blog"
[languages]
[languages.pt]
languageName = "Português"
title = "Nome do seu blog"
[[languages.pt.menu.main]]
name = "Início"
identifier = "home"
url = "/"
weight = 1
[[languages.pt.menu.main]]
name = "Posts"
identifier = "posts"
url = "/posts"
weight = 2
[[languages.pt.menu.main]]
name = "Sobre"
identifier = "about"
url = "/about"
weight = 3
[markup]
[markup.tableOfContents]
startLevel = 2
endLevel = 3
ordered = false
Substitua os valores óbvios — baseURL, title, subtitle, logoText — pelos seus. O baseURL vai mudar quando você configurar o domínio definitivo, mas por enquanto pode deixar qualquer coisa; o importante é que não fique vazio.
O parâmetro showMenuItems controla quantos itens aparecem no menu principal antes de os demais serem jogados num submenu. Se você tem três itens no menu, coloque 3. Se adicionar um quarto depois, atualize para 4.
Primeiro post de teste#
Crie a estrutura de pastas para os posts e a página “Sobre”:
mkdir -p content/posts
Crie o arquivo content/posts/primeiro-post.md com o seguinte conteúdo:
---
title: "Meu primeiro post"
date: 2026-03-26T10:00:00
description: "Um post de teste para verificar se tudo funciona."
tags:
- "teste"
slug: como-criei-este-blog-sem-gastar-um-centavo-e-sem-tocar-em-wordpress
draft: false
toc: false
---
Se você está lendo isso, o Hugo está funcionando.
## Um subtítulo de teste
Aqui vai um parágrafo normal com **negrito** e *itálico*.
### Um sub-subtítulo
E uma lista:
- Item um
- Item dois
- Item três
Pronto. Se isso renderizou corretamente, o tema está
configurado e podemos seguir em frente.
Crie também a página content/about.md:
---
title: "Sobre"
slug: como-criei-este-blog-sem-gastar-um-centavo-e-sem-tocar-em-wordpress
draft: false
---
Escreva aqui uma breve descrição sobre você ou sobre o blog.
Esta página é acessível pelo menu principal.
Testando localmente antes de publicar#
Rode o servidor de desenvolvimento do Hugo:
hugo server -D
O -D inclui posts marcados como rascunho. Abra http://localhost:1313 no navegador e você deve ver o site com o tema aplicado, o menu funcionando, e o post de teste na listagem.
Navegue pelo site. Clique no post para ver se abre. Verifique se o menu mostra os itens que você configurou. Se algo não estiver certo, edite o hugo.toml e salve — o Hugo recarrega automaticamente e o navegador atualiza sozinho.
Quando estiver satisfeito, é hora de enviar para o GitHub:
git add .
git commit -m "Setup inicial: Hugo + tema + primeiro post"
git push
A partir deste ponto, seu site existe como código no GitHub. Na próxima seção, vamos colocá-lo no ar.
Colocando no ar com Cloudflare#
Até agora, o site só existe na sua máquina e no GitHub. Ninguém mais consegue vê-lo. O Cloudflare é quem vai transformar o repositório num site público, acessível por qualquer navegador do mundo.
Conectando o repositório#
Acesse dash.cloudflare.com e, no menu lateral, clique em Workers & Pages. Clique em Create application e escolha a opção de importar um repositório do GitHub. Se é a primeira vez que você conecta o Cloudflare ao GitHub, ele vai pedir para instalar o app “Cloudflare Workers and Pages” na sua conta. Durante a instalação, o GitHub pergunta a quais repositórios o Cloudflare terá acesso — você pode liberar todos ou selecionar apenas o repositório do seu blog. Recomendo selecionar apenas o que precisa; você pode adicionar outros depois.
Um detalhe que pega muita gente: se você criar um novo repositório depois de já ter feito essa configuração, o Cloudflare não vai enxergá-lo automaticamente. Você precisa voltar às configurações do GitHub App (em github.com/settings/installations), clicar em “Configure” ao lado de “Cloudflare Workers and Pages”, e adicionar o novo repositório à lista de permissões.
Com o repositório visível, selecione-o e siga para a tela de configuração do build.
Configurando o build#
A tela de configuração pede algumas informações. A interface do Cloudflare muda com certa frequência, então os nomes exatos dos campos podem variar, mas o conceito é sempre o mesmo:
O Build command é o comando que o Cloudflare vai executar para gerar o site. Use:
hugo --minify
O --minify é opcional, mas reduz o tamanho do HTML gerado removendo espaços e quebras de linha desnecessárias. Não afeta a aparência do site, só diminui o tamanho dos arquivos.
Você vai precisar de um arquivo wrangler.toml na raiz do seu repositório para que o Cloudflare saiba onde encontrar o resultado do build. Crie o arquivo com este conteúdo:
name = "nome-do-seu-projeto"
compatibility_date = "2026-03-22"
[assets]
directory = "./public"
A seção [assets] com directory = "./public" é a parte que diz ao Cloudflare que o conteúdo a ser publicado está na pasta public/ — que é exatamente onde o Hugo coloca o HTML gerado.
Nas configurações de variáveis de ambiente (normalmente escondidas atrás de um botão “Advanced settings” ou similar), adicione uma variável chamada HUGO_VERSION com o valor da versão que você instalou na sua máquina — por exemplo, 0.158.0. Sem essa variável, o Cloudflare vai usar uma versão antiga do Hugo e o build pode falhar silenciosamente ou gerar um site diferente do que você viu localmente.
Faça commit do wrangler.toml e push:
git add wrangler.toml
git commit -m "Adiciona wrangler.toml para Cloudflare"
git push
O primeiro deploy (e os erros que você vai encontrar)#
Clique em Deploy e acompanhe o log. O Cloudflare vai clonar o repositório, instalar o Hugo, executar o build, e publicar o resultado. O primeiro deploy leva entre um e dois minutos.
Se tudo correr bem, o log termina com uma mensagem de sucesso e uma URL no formato nome-do-projeto.seu-subdominio.workers.dev. Abra no navegador e confira — o site deve ser idêntico ao que você viu no localhost:1313.
Mas convém estar preparado para as coisas que costumam dar errado na primeira tentativa, porque quase ninguém acerta de primeira:
“Unable to locate config file” significa que o Cloudflare está rodando o Hugo no diretório errado. Verifique se o campo “Path” na configuração de build está como / (a raiz do repositório) e não apontando para algum subdiretório.
“Error building site: template failed” geralmente indica incompatibilidade entre a versão do Hugo e o tema. Temas mais antigos usam funções que o Hugo removeu em versões recentes. Confirme que a variável HUGO_VERSION está configurada e que o valor corresponde à versão que funciona na sua máquina.
“Authentication error” durante o deploy pode indicar que o token de API gerado automaticamente não tem as permissões necessárias. Se isso acontecer, tente recriar o projeto do zero — às vezes a interface do Cloudflare tropeça na primeira configuração e funciona perfeitamente na segunda.
O importante é saber que o build do Hugo e o deploy para o Cloudflare são etapas separadas. Se o log mostra “Build command completed” seguido de um erro no deploy, o problema não é no seu site — é na comunicação entre o Cloudflare e a sua conta. Se o erro aparece durante o build, antes do “Build command completed”, o problema está na configuração do Hugo ou no tema.
Uma vez que o primeiro deploy funciona, os seguintes são automáticos. Cada push no repositório — seja do seu terminal, seja do Pages CMS — dispara um novo build e deploy sem que você precise tocar em nada. O ciclo inteiro, do commit ao site atualizado, leva entre um e dois minutos.
Com o site no ar, atualize o baseURL no hugo.toml para a URL que o Cloudflare gerou, faça commit e push. Esse detalhe é fácil de esquecer, mas o Hugo usa o baseURL para gerar links internos, o sitemap e o feed RSS — se estiver errado, esses recursos vão apontar para o lugar errado.
Conectando o Pages CMS#

O site está no ar e o deploy automático funciona. O que falta é a peça que torna tudo isso utilizável por alguém que não sabe (e não quer saber) o que é Git: o editor web.
O arquivo .pages.yml — o mapa do seu conteúdo#
O Pages CMS não adivinha a estrutura do seu site. Você precisa dizer a ele onde ficam os posts, quais campos cada post tem, e onde as imagens são armazenadas. Tudo isso vai num único arquivo chamado .pages.yml, na raiz do repositório.
Um exemplo funcional para um blog Hugo:
media:
input: static/img
output: /img
content:
- name: posts
label: Posts
type: collection
path: content/posts
view:
fields: [title, date]
fields:
- name: title
label: Título
type: string
- name: date
label: Data
type: date
- name: description
label: Resumo
type: string
- name: cover
label: Imagem de capa
type: image
- name: tags
label: Tags
type: string
list: true
- name: toc
label: Índice de conteúdo
type: boolean
default: true
- name: draft
label: Rascunho
type: boolean
default: false
- name: body
label: Conteúdo
type: code
options:
language: markdown
Cada bloco dentro de fields corresponde a um campo que o autor vai ver no editor. O type define o tipo de controle — string é um campo de texto simples, date mostra um seletor de data, image permite upload, boolean é uma chave liga/desliga, e code com a opção language: markdown oferece um editor de texto com destaque de sintaxe para Markdown.
A seção media merece atenção. O input é o caminho dentro do repositório onde as imagens são salvas — static/img. O output é o caminho como o Hugo vai servir esses arquivos no site final — /img. Essa distinção existe porque o Hugo serve tudo que está dentro de static/ a partir da raiz do site, eliminando o prefixo static da URL. Se você configurar media: static/img sem separar input e output, as imagens vão funcionar no editor mas dar 404 no site publicado.
Faça commit e push do arquivo:
git add .pages.yml
git commit -m "Adiciona configuração do Pages CMS"
git push
Acessando o editor pela primeira vez#
Abra app.pagescms.org e faça login com sua conta do GitHub. O Pages CMS vai pedir permissão para acessar os seus repositórios — assim como o Cloudflare, ele funciona como um GitHub App, e você pode restringir o acesso a repositórios específicos.
Depois de autorizar, a tela principal mostra a lista de projetos. Selecione o repositório do seu blog e o Pages CMS vai ler o .pages.yml para montar a interface. Se tudo estiver correto, você vai ver “Posts” no menu lateral e, ao clicar, a lista de posts existentes no repositório.
Se o repositório não aparecer na lista, o problema é o mesmo do Cloudflare: o GitHub App do Pages CMS não tem acesso ao repositório. Vá em github.com/settings/installations, encontre o app do Pages CMS, e adicione o repositório.
Criando e publicando um post pelo navegador#
Clique em “Add an entry” no canto superior direito. O editor vai mostrar os campos que você definiu no .pages.yml — título, data, resumo, imagem de capa, tags, e o campo de conteúdo em Markdown.
Preencha os campos e escreva o post no editor Markdown. O campo usa o Codemirror com destaque de sintaxe, então títulos, links, negrito e código aparecem coloridos enquanto você digita. Não é um editor WYSIWYG com preview visual ao vivo, mas para quem está acostumado com Markdown a experiência é fluida.
Quando terminar, clique em “Save”. O Pages CMS vai criar o arquivo Markdown no repositório com o front matter preenchido e o conteúdo do post. Esse commit dispara o webhook para o Cloudflare, que roda o build do Hugo e publica a versão atualizada do site. Em um ou dois minutos, o post está no ar.
Uma nota sobre apagar posts: o Pages CMS tem um bug conhecido na função de exclusão — o post aparenta ser removido no painel, mas o arquivo permanece no repositório, e o post continua aparecendo no site. Até que isso seja corrigido, a forma segura de “despublicar” um post é editar o post e marcar o campo Rascunho como ativo. O Hugo ignora posts marcados como draft no build de produção, então o post desaparece do site sem precisar apagar o arquivo. Se você realmente precisar excluir um post do repositório, faça pelo terminal com git rm e push.
Configurando imagem destacada#
O campo cover no .pages.yml permite que o autor faça upload de uma imagem que será usada como destaque do post — na listagem, no topo do artigo, e nos cards de compartilhamento em redes sociais. O comportamento exato depende do tema: o Terminal usa o campo cover no front matter, o Mainroad usa thumbnail, e outros temas podem ter nomes diferentes. Consulte a documentação do tema que escolheu para saber o nome correto.
Ao clicar no campo de imagem no editor, o Pages CMS permite selecionar uma imagem já existente na pasta de mídia ou fazer upload de uma nova. A imagem é salva no repositório no caminho configurado em media.input, e o caminho gravado no front matter do post segue o padrão definido em media.output.
O detalhe do caminho das imagens#
Este é o erro mais comum na integração entre o Pages CMS e o Hugo, e vale reforçar porque ele é silencioso — tudo parece funcionar no editor, mas as imagens aparecem quebradas no site.
O Hugo serve os arquivos da pasta static/ diretamente na raiz do site. Um arquivo em static/img/foto.jpg fica acessível em seusite.com/img/foto.jpg, e não em seusite.com/static/img/foto.jpg. Se o Pages CMS gravar o caminho completo do repositório no front matter (/static/img/foto.jpg), o navegador vai pedir um arquivo que não existe naquele caminho e mostrar uma imagem quebrada.
A solução é a separação entre input e output na configuração de mídia do .pages.yml que mostramos acima. O input: static/img diz ao Pages CMS onde salvar o arquivo no repositório. O output: /img diz qual caminho gravar no front matter do post. Quando o Hugo gera o site, o arquivo está em static/img/ e a referência no HTML aponta para /img/ — e tudo bate.
Domínio próprio#
O site funciona no subdomínio .workers.dev que o Cloudflare gerou, mas ninguém vai levar a sério um blog com uma URL que parece um projeto de teste. Colocar o domínio próprio é o toque final que transforma o setup num site de verdade.
Apontando o DNS#
Se o seu domínio já está no Cloudflare (como nameserver ou com proxy ativo), o processo é direto. No dashboard do Cloudflare, vá em DNS → Records e adicione um registro CNAME:
- Type: CNAME
- Name: o subdomínio que você quer (por exemplo,
devopsparadevops.sarmento.org, ou@para usar o domínio raiz) - Target: a URL que o Cloudflare gerou para o seu projeto, sem o
https://— algo comonome-do-projeto.seu-subdominio.workers.dev - Proxy status: Proxied (nuvem laranja ativa)
Depois, vá em Workers & Pages, abra o projeto do seu site, e em Settings procure a seção de domínios customizados (Custom Domains). Adicione o domínio — por exemplo, devops.sarmento.org. O Cloudflare vai verificar que o CNAME existe e associar o domínio ao projeto.
Se o seu domínio está registrado em outro lugar (Registro.br, GoDaddy, Namecheap), você tem duas opções. A mais simples é transferir os nameservers para o Cloudflare — o plano gratuito inclui DNS hosting, e a propagação leva de minutos a poucas horas. A alternativa é criar o CNAME no painel do seu registrador atual apontando para o endereço .workers.dev, mas nesse caso você perde o proxy do Cloudflare e algumas otimizações de cache.
A propagação de DNS pode levar de poucos minutos até 24 horas, dependendo do TTL configurado e do provedor. Na prática, com o Cloudflare como nameserver, a mudança costuma refletir em menos de cinco minutos.
HTTPS automático#
Não precisa fazer nada. O Cloudflare gera e renova automaticamente um certificado SSL para o seu domínio customizado. Assim que o DNS propagar e o Cloudflare reconhecer o domínio, o site passa a responder em HTTPS sem configuração adicional, sem instalar certificado, e sem renovação manual.
Se alguém acessar o site por HTTP, o Cloudflare redireciona automaticamente para HTTPS. Isso já vem habilitado por padrão — mas se por algum motivo não estiver, vá em SSL/TLS → Edge Certificates e ative “Always Use HTTPS”.
Depois de confirmar que o domínio está funcionando, atualize o baseURL no hugo.toml para o endereço definitivo, faça commit e push. Essa é a última vez que você precisa mexer nesse campo.
E as coisas que o WordPress fazia sozinho?#
Quem vem do WordPress está acostumado a resolver tudo com plugins. Busca, comentários, formulário de contato, posts agendados — tudo existe a um clique de distância no painel. Num site estático, essas funcionalidades não vêm de graça, mas também não são impossíveis. Algumas são surpreendentemente fáceis; outras exigem compromissos.
Busca em site estático — como funciona sem banco de dados#
A primeira reação de quem descobre sites estáticos costuma ser: “mas como é que busca funciona sem banco de dados?” A resposta é que a busca roda inteiramente no navegador do visitante.
Durante o build, o Hugo pode gerar um arquivo de índice em JSON contendo os títulos, resumos e (opcionalmente) o conteúdo completo de todos os posts. Quando o visitante abre a página de busca e digita alguma coisa, um JavaScript carrega esse índice e filtra os resultados ali mesmo, sem nenhuma requisição ao servidor.
A solução mais comum é o Fuse.js — uma biblioteca leve de fuzzy search que funciona bem para blogs de pequeno e médio porte. O visitante digita, os resultados aparecem instantaneamente enquanto ele tecla. Vários temas Hugo já trazem busca integrada com Fuse.js; basta ativar na configuração.
O ponto de atenção é o tamanho do índice. Se o blog tem 50 posts, o JSON pesa poucos kilobytes e carrega imperceptivelmente. Com 500 posts indexando conteúdo completo, o arquivo pode passar de 2 ou 3 MB — o visitante precisa baixar isso antes de a busca funcionar. A solução para sites grandes é o Pagefind, uma ferramenta que gera um índice binário fragmentado e só baixa os pedaços relevantes para cada busca. Ele se integra com o Hugo sem dificuldade e escala para milhares de páginas sem que o visitante perceba qualquer lentidão.
Comentários sem backend — Giscus, Utterances e alternativas#
Comentários são a funcionalidade que mais falta faz quando se sai do WordPress, e ao mesmo tempo a que mais dor de cabeça causa em qualquer plataforma. Spam, moderação, GDPR, performance — o sistema de comentários nativo do WordPress resolve o básico, mas qualquer instalação séria acaba recorrendo a plugins como Akismet ou Disqus de qualquer forma.
Num site estático, comentários precisam de um serviço externo. As opções mais populares e gratuitas:
O Giscus usa o sistema de Discussions do GitHub como backend. Cada post do blog vira uma discussion no repositório, e os comentários são threads dentro dela. O visitante precisa ter conta no GitHub para comentar, o que é uma barreira para público não técnico, mas um filtro natural contra spam para blogs de tecnologia. A integração é um trecho de JavaScript no template do tema.
O Utterances funciona de forma parecida, mas usa Issues do GitHub em vez de Discussions. Cada post gera uma issue, e os comentários são respostas nela. A experiência é quase idêntica à do Giscus, com a mesma limitação de exigir conta GitHub.
O Disqus é o mais universal — aceita login por várias redes sociais e email — mas injeta scripts pesados, rastreia visitantes, e exibe anúncios no plano gratuito. Se o público do blog não é técnico e precisa de uma experiência familiar de comentários, o Disqus funciona, mas o custo é performance e privacidade.
Para blogs técnicos como este, Giscus é a melhor escolha. Para blogs voltados ao público geral, o melhor pode ser simplesmente não ter comentários e direcionar a conversa para redes sociais — que é o que a maioria dos grandes sites de notícias faz hoje. Quem prefere controle total sobre os dados pode considerar o Isso, um servidor de comentários self-hosted que não depende de nenhuma plataforma.
Posts relacionados — o que o Hugo oferece nativamente#
O Hugo tem um sistema nativo de posts relacionados que funciona sem nenhum plugin. Ele analisa tags, categorias, data de publicação e palavras-chave do front matter para calcular uma pontuação de relevância entre os posts e exibir uma lista de sugestões no final de cada artigo.
A configuração vai no hugo.toml:
[related]
includeNewer = true
threshold = 80
toLower = true
[[related.indices]]
name = "tags"
weight = 100
[[related.indices]]
name = "categories"
weight = 80
[[related.indices]]
name = "date"
weight = 10
O threshold define a pontuação mínima para um post aparecer como relacionado — quanto mais alto, mais restritivo. Os pesos em cada índice controlam o que pesa mais na comparação: neste exemplo, tags compartilhadas valem mais do que categorias, e a data de publicação tem influência mínima.
A exibição depende do tema. Alguns temas já incluem um bloco de posts relacionados no layout do post; outros exigem que você adicione o trecho no template. No pior caso, são poucas linhas de Go template para inserir a lista — a documentação do Hugo cobre isso em detalhe.
Posts agendados — publicar no futuro sem cron job#
O Hugo tem suporte nativo a datas futuras. Se o front matter de um post tem uma data posterior ao momento do build, o Hugo simplesmente não inclui o post no site gerado. O post existe no repositório, mas é invisível para os visitantes.
O problema é que alguém precisa disparar o build depois que a data chega. No WordPress, o cron interno cuida disso automaticamente. Num site estático, o build só acontece quando há um push no repositório ou quando alguém o dispara manualmente.
A solução mais simples é um cron job no Cloudflare ou uma GitHub Action agendada. Uma Action que roda uma vez por dia (ou a cada hora, dependendo da sua necessidade) faz um push vazio no repositório, o que dispara o build no Cloudflare. Se houver posts cuja data já passou, eles aparecem no site; se não, o build termina sem mudar nada.
name: Publicar posts agendados
on:
schedule:
- cron: '0 */6 * * *'
jobs:
trigger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git commit --allow-empty -m "Trigger build para posts agendados"
git push
Esse workflow roda a cada seis horas e faz um commit vazio — suficiente para disparar o build sem alterar nenhum conteúdo. A granularidade é ajustável; se precisar que posts entrem no ar com precisão de hora, mude o cron para rodar a cada hora. Para a maioria dos blogs, uma vez por dia é suficiente.
Anúncios — onde e como inserir nos layouts e nos posts#
Monetização com anúncios funciona normalmente num site estático — o HTML gerado pelo Hugo é idêntico ao que qualquer CMS dinâmico produziria, e as redes de anúncios (Google AdSense, Ezoic, Mediavine) não distinguem um do outro. O que muda é onde você coloca o código.
No WordPress, plugins como Ad Inserter permitem posicionar anúncios sem mexer em código. Num site Hugo, os anúncios vão diretamente nos templates do tema. Existem dois lugares naturais:
Para anúncios no layout (sidebar, cabeçalho, rodapé, entre posts na listagem), você edita os partials do tema. A maioria dos temas bem estruturados tem arquivos como layouts/partials/header.html, layouts/partials/footer.html, e layouts/partials/sidebar.html. O código do anúncio — normalmente um trecho de JavaScript fornecido pela rede de anúncios — vai direto nesses arquivos, na posição desejada.
Para anúncios dentro do conteúdo dos posts (por exemplo, depois do terceiro parágrafo), a abordagem mais limpa é criar um shortcode. Crie o arquivo layouts/shortcodes/ad.html com o código do anúncio, e no Markdown do post insira {{< ad >}} onde quiser que o anúncio apareça. Isso mantém o conteúdo separado da monetização — se você trocar de rede de anúncios, altera um arquivo em vez de editar centenas de posts.
Formulário de contato — opções sem servidor#
Um site estático não processa formulários — não tem backend para receber os dados. Mas existem serviços especializados que resolvem isso com uma única linha de configuração:
O Formspree é o mais simples. Você cria um formulário HTML normal no seu site, aponta o action para o endpoint do Formspree, e os envios chegam no seu e-mail. O plano gratuito permite 50 envios por mês, o que é mais do que suficiente para um blog.
O Formspark e o Basin funcionam de forma parecida, com planos gratuitos de diferentes capacidades. O Cloudflare Workers também pode processar formulários, o que mantém tudo dentro do mesmo ecossistema — mas exige escrever um pouco de JavaScript.
Para a maioria dos blogs, um formulário simples com Formspree resolve sem nenhuma complicação. Adicione uma página content/contato.md com o formulário em HTML puro no corpo do Markdown — o Hugo renderiza HTML dentro de Markdown sem problemas.
RSS — o feed que já vem pronto#
Essa é a boa notícia mais curta do post: o Hugo gera RSS automaticamente. Não precisa instalar nada, não precisa configurar nada. O feed fica disponível em /index.xml por padrão e inclui todos os posts do site com título, data, resumo e link.
Se quiser personalizar o que aparece no feed — por exemplo, incluir o conteúdo completo em vez de só o resumo, ou limitar a quantidade de itens — o Hugo permite sobrescrever o template RSS. Mas para a maioria dos blogs, o padrão funciona perfeitamente desde o primeiro build.
Basta adicionar o link no menu de navegação ou no rodapé do site para que os leitores encontrem. Agregadores de RSS como Feedly, Inoreader e NewsBlur reconhecem o formato automaticamente.
O que aprendi no caminho#
Vantagens que não esperava#
A vantagem mais óbvia — velocidade — eu já esperava. Um site estático servido por CDN carrega rápido, e pronto. O que me surpreendeu foram os benefícios colaterais que só aparecem depois de usar o setup por um tempo.
O versionamento de conteúdo é o primeiro. No WordPress, se um editor sobrescreve um parágrafo e salva, a versão anterior fica enterrada num sistema de revisões que quase ninguém sabe usar. Com o conteúdo no Git, cada alteração é um commit com data, autor e diff. Se alguém publicar algo errado às duas da manhã, reverter é um git revert — ou, no pior caso, um clique no histórico do GitHub. Isso parece um detalhe técnico até o dia em que salva o seu pescoço.
O custo operacional foi a segunda surpresa. Não é só a hospedagem que sai de graça — é a ausência total de manutenção de infraestrutura. Não tem servidor para atualizar, não tem PHP para manter compatível, não tem MySQL para otimizar, não tem plugin de cache para configurar. O site simplesmente existe como um conjunto de arquivos estáticos distribuídos pelo mundo. A conta mensal de infraestrutura é zero. A conta mensal de horas gastas apagando incêndio também é zero.
A terceira foi a portabilidade. O conteúdo inteiro do site é um repositório Git com arquivos Markdown. Se o Cloudflare desaparecer amanhã, eu mudo o deploy para Netlify, Vercel, ou um Nginx no meu próprio servidor em menos de uma hora. Se o Pages CMS fechar, os arquivos continuam no GitHub — eu só preciso de outro editor, ou de um editor de texto qualquer. Nenhuma peça do sistema é insubstituível, e nenhuma delas segura os meus dados como refém.
Limitações que você precisa conhecer#
A limitação mais concreta é a ausência de funcionalidades dinâmicas nativas. Tudo o que depende de processamento no servidor — busca avançada, comentários, formulários, área restrita — precisa de um serviço externo ou de um workaround. As soluções existem (e cobrimos as principais neste post), mas cada uma adiciona uma dependência e um ponto de configuração. No WordPress, é um plugin. Aqui, é um serviço externo com conta própria, documentação própria, e limites próprios.
O Pages CMS, apesar de funcional, ainda é um projeto jovem mantido essencialmente por uma pessoa. O editor rich-text não existe — o campo de conteúdo é um editor de código com syntax highlighting de Markdown, o que é aceitável para quem conhece a sintaxe, mas inviável para um redator que nunca viu um ## na vida. A função de excluir posts tem um bug que deixa o arquivo no repositório mesmo depois de confirmar a exclusão. São problemas contornáveis, mas que mostram que a ferramenta ainda não está pronta para entregar a um cliente não técnico sem supervisão.
O build não é instantâneo. Entre o clique em “salvar” e o post aparecer no site, passam-se um a dois minutos. Para um blog, isso é irrelevante. Para um site de notícias que precisa publicar com urgência de breaking news, pode ser um incômodo. O Hugo compila em menos de um segundo — a latência está toda no Cloudflare provisionando o ambiente e distribuindo os arquivos.
Editar o tema exige mexer em templates Go. O sistema de templates do Hugo é poderoso, mas a sintaxe não é intuitiva para quem vem do PHP do WordPress. Coisas simples como mudar a posição de um elemento na página ou adicionar um campo customizado no layout do post significam abrir um arquivo .html cheio de {{ }} e entender a lógica de partials, blocos e contextos. A curva de aprendizado não é íngreme, mas existe, e aparece justamente na hora em que você quer fazer algo que o tema não previu.
Para quem essa abordagem faz sentido (e para quem não faz)#
Essa stack faz sentido para blogs pessoais e profissionais, sites de documentação, portfólios, sites institucionais, e pequenos sites de notícias. Faz sentido especialmente quando o autor é técnico ou tem acesso a alguém técnico que configure o sistema inicial — porque a configuração exige terminal, Git, e leitura de documentação. Depois de configurado, o dia a dia é simples o bastante para qualquer pessoa que saiba preencher um formulário web.
Não faz sentido para sites que dependem de interatividade pesada em tempo real: e-commerce com carrinho de compras, aplicações web com login de usuário, fóruns de discussão, plataformas de e-learning com progresso de aluno. Essas funcionalidades precisam de um backend, e tentar replicá-las com serviços externos colados num site estático é nadar contra a corrente.
Também não faz sentido — pelo menos por enquanto — quando os autores são completamente não técnicos e não existe ninguém disponível para resolver os inevitáveis tropeços. O Pages CMS é amigável, mas não é WordPress. Não tem dashboard com métricas, não tem preview visual antes de publicar, não tem lixeira que recupera posts apagados por engano. Quem opera o site precisa ter um mínimo de conforto com ferramentas digitais e, idealmente, acesso a alguém que saiba abrir um terminal quando as coisas derem errado.
Para todo o resto — para quem quer um site rápido, seguro, barato de operar, e que não exija vigiar um servidor 24 horas por dia — essa combinação de Hugo, Pages CMS e Cloudflare entrega mais do que o necessário com menos dor de cabeça do que qualquer alternativa dinâmica que eu já administrei.
Se quiser entender onde estão os limites reais dessa stack gratuita — e o que fazer quando esbarrar neles — escrevi um post dedicado ao assunto. E se você vem do WordPress e quer entender por que temas no Hugo funcionam de forma tão diferente, o post De WordPress a Hugo: temas não são o que você pensa mapeia as diferenças conceituais. E quando o número de posts crescer e manter tags e meta descriptions consistentes virar um problema, o Hugin usa IA para classificar e resumir posts Hugo automaticamente.
Sysadmin, 53 anos, brasileiro trabalhando de casa para o mundo todo. Cuida de servidores Linux, containers LXC, e de gatos que não saem de cima do teclado.