Existe um grupo de pessoas que sonha com a possibilidade de gerar versões "HTML estáticas" de seus sites tradicionalmente mantidos em um CMS dinâmico (Content Management System — Sistema de Gestão de Conteúdo).

Quem fizer uma busca pela Internet vai encontrar algumas dezenas de tutoriais propondo-se a atender esta demanda; dos que eu acompanhei (apenas analisando ou tentando implementar) nenhum conseguiu cumprir o que prometia, fosse por ignorar alguns itens fundamentais, fosse por terem sido elaborados para atender uma necessidade muito diferente da minha.

CMSs dinâmicos × CMSs estáticos

Há dois tipos de gerenciadores de conteúdo, basicamente: os dinâmicos e os estáticos.

Os gerenciadores dinâmicos são sistemas de gerenciamento (voltados para o "mercado" editorial), normalmente em cima de um banco de dados. Cada requisição feita ao site requer que o sistema consulte o banco de dados e gere a página correspondente para exibir para o visitante.

Já os CMSs estáticos funcionam de maneira diferente: em vez de ser um aplicativo rodando o tempo inteiro e respondendo a requisições de visitantes ele gera o site inteiro de uma só vez: toda e qualquer página possível de ser acessada no site estará necessariamente "pronta" antes mesmo da publicação do site (disponibilização dos arquivos no servidor). Assim, quando o visitante requisitar uma página qualquer ela simplesmente estará prontinha para ser entregue, sem esperas, sem atrasos, sem processamento nenhum a não ser o de entregar o conteúdo ao navegador.

Propositalmente não estou abordando a questão dos caches para CMSs dinâmicos neste momento.

As vantagens dos CMSs dinâmicos são bastante evidentes, e quem está acostumado ao WordPress, ao Joomla, Drupal, Ghost, ou qualquer dos muitos existentes, já as conhece todas. Destaco:

  • facilidade de trabalhar em equipe: várias pessoas podem trabalhar ao mesmo tempo em matérias diferentes (ou até na mesma, com alguma criatividade); o mesmo se aplica à possibilidade de trabalhar de qualquer lugar que tenha conexão à Internet;
  • publicação instantânea: ao clique de um botão qualquer alteração feita em qualquer aspecto do site estará disponível, sem qualquer tipo de espera ou atraso;
  • ferramentas avançadas: em CMSs dinâmicos é muito fácil de inserir recursos como artigos relacionados, páginas personalizadas, etc.

Já as vantagens dos CMS estáticos podem não ser tão evidentes, principalmente para quem tem blogs pequenos (em tamanho e em visitação). Eu destaco as seguintes:

  • custo extremamente baixo: o servidor web para entregar conteúdo estático na Internet pode ser extremamente modesto, uma vez que não há sequer a necessidade de suporte a linguagens de script, nem banco de dados, nada;
  • segurança extrema: como o site que é publicado não tem um sistema de gerenciamento público os riscos de invasão por meio de vulnerabilidades deste se reduzem a zero; a única preocupação com segurança, relacionada ao site em si, diz respeito à segurança do servidor, o que costuma ser responsabilidade das empresas de hospedagem;
  • desempenho extremo: como tudo o que vai ser entregue aos visitantes está previamente "compilado" os recursos de processamento do servidor ficam disponíveis para tratar o tráfego de rede, firewall, etc.

Prova de Conceito

Este blog, desde o seu nascimento, tem sido mantido usando um CMS estático. De todos os que experimentei o que mais me agradou foi o Hugo. Entretanto, "mais me agradou" também pode ser lido como "o que menos me estressou", e eu sei que isso é resultado do "vício" em WordPress e suas facilidades.

Entretanto, o meu mecanismo de blogs favorito é o Ghost, que nasceu para ser justamente uma alternativa mais leve, mais focada em conteúdo, do que o onipresente WordPress. Desde que eu o usei pela primeira vez fiquei apaixonado pela simplicidade e pelo desempenho (embora não seja a coisa mais fácil do mundo, para um não técnico, fazê-lo funcionar corretamente).

Resolvi, então, dar um jeito de transportar o conteúdo do blog dos meus arquivos estáticos (todos armazenados no Dropbox, para facilitar trabalhar de qualquer lugar) para uma instalação do Ghost que inicialmente estava em um VPS, mas agora está no meu próprio computador. Essa foi a parte fácil.

A dificuldade mesmo veio ao tentar converter o blog para site estático, considerando, principalmente, que este blog roda sob HTTPS.

Entra o Buster

Quem é do mundo Linux (ou Unix, de maneira geral) conhece o comando wget --mirror que teoricamente é capaz de baixar uma cópia integral de qualquer site publicado para outro local.

Pois o wget é o núcleo de um software chamado Buster, que se define como um sistema de criação de sites estáticos a partir do Ghost usando força bruta.

Escrito em Python, o que o Buster faz é rodar um wget --mirror e consertar algumas coisinhas antes de que o site possa ser publicado, tais como ajustar o domínio das URLs e remover as querystrings dos CSSs e JavaScripts. Lindo, na teoria, mas há dois problemas

O primeiro problema que notei diz respeito, creio, ao Python em si: no momento que o programa altera cada página para aplicar as correções os caracteres acentuados ficam totalmente bagunçados. Para quem só escreve em Inglês, ou outro idioma sem caracteres especiais, isso não deve fazer diferença, mas para mim foi desesperador.

O segundo parece ser ligado ao wget, porque na segunda abordagem que fiz o problema se repetiu: o site não era baixado inteiro a partir do Ghost, especialmente as URLs de paginação (tais como /page/2, /page/3, etc).

Meu próprio script

Como o Buster não me serviu (e olha que foi um parto para fazê-lo funcionar no OS X El Capitán, mas consegui obter sucesso usando o Anaconda) acabei resolvendo fazer um script em Bash que cumprisse a tarefa de baixar o site inteiro, trocar as URLs em cada página e tornar o resultado final em algo pronto para ser publicado.

Na verdade, a versão final do meu script já publica o site e otimiza as imagens automaticamente por mim.

O coração do sistema são as três linhas abaixo:

#!/bin/bash

remoteurl='https://wp.sarmento.org'  
DIR=`echo ${remoteurl} | sed -e 's,http://,,g' | sed -e 's/:/_/g'`

# Baixa o site todo
wget --recursive --convert-links --page-requisites --no-parent \  
  --directory-prefix static --no-host-directories --restrict-file-name=unix $remoteurl

# Converte o domínio para URLs relativas
grep -rl ${remoteurl} ${DIR} | xargs sed -i '' -e s,${remoteurl}/,/,g  

Vale observar que o sed acima usa a sintaxe do OS X, que é diferente da do GNU sed, usado no Linux.

Não demorei nada a perceber que embora meu script não tivesse o problema da acentuação ele também sofria do mal de não baixar as páginas todas, gerando um site cheio de páginas 404.

Cometi algumas gambiarras vergonhosas para tentar contornar esse problema, porém o nível de complexidade do código saltou a níveis absurdos, o que certamente comprometeria a facilidade de se dar manutenção no código, ou mesmo de usá-lo em situações fora do meu ambiente controlado.

Por fim, consegui resolver o problema do sumiço das páginas trocando o wget pelo httrack. Httrack é um "navegador offline" disponível para Windows, Mac e Linux, que tem por função justamente baixar sites inteiros para o disco local para propiciar navegação offline.

Mas o uso do httrack se por um lado resolveu o sumiço das páginas baixadas por outro criou novos problemas: no momento em que redijo este parágrafo este blog tem cerca de 80 páginas, sendo uns 30 posts e o restante páginas archive de autor e de marcadores. O tempo que um blog pequeno assim leva para ser baixado pelo httrack fica na ordem de uns cinco minutos sem trafegar nada pela Internet (o Ghost roda na minha própria máquina, reitero).

Além disso experimentei um outro problema cuja lógica não consigo encontrar: os arquivos que estão na minha máquina não referenciam nenhum objeto por http, o que é necessário para evitar erros mixed content por causa do https do meu site. Mas depois que os arquivos são enviados para o servidor todas as URLs em toda parte são convertidas para http outra vez, demandando um trabalho extra de consertar no servidor todos estes problemas.

É por esta razão que não vou divulgar o código do meu script aqui. Embora seja praxe eu dizer que não garanto nem dou suporte a nenhum código publicado, seria sacanagem divulgar um script que eu sei que vai dar pau (e depois ainda recusar-me a ajudar quem venha a tentar).

Conclusão

Infelizmente, a não ser pela prova de conceito, que por natureza ocorre em ambiente controlado e em pequena escala, os problemas de se tornar um site dinâmico (não importa qual o CMS usado) em estático tornam a tarefa especialmente difícil, criando um grau de dificuldade que considero acima do tolerável para aplicações no dia a dia.

Se este blog, com trinta posts, demora cerca de cinco minutos para ser espelhado pelo httrack (numa máquina com processador i7, 16GB de RAM, rodando tudo em localhost), podemos inferir que um blog com 300 posts — e isso não é nada — vá demorar mais de 8h apenas para ser espelhado. Some a isso as imagens usadas no site, e temos uma possibilidade de uso para a hashtag #medo.

Além disso, não há como garantir que o httrack vá baixar integralmente todas as páginas do site. CMSs dinâmicos podem ter URLs imprevisíveis num primeiro momento, mas que podem ser corretamente tratadas e exibidas pelo sistema.

Assim, vejo como inviável converter um site dinâmico para estático. Até mesmo este blog, usado como prova de conceito, não vai demorar a ser inviável para o meu script, demandando ser instalado como um Ghost regular.

Quem quiser ter sites 100% estáticos sem sustos vai ter que experimentar um dos muitos CMSs estáticos existentes.