MÓDULO 6.3

🚀 GitHub Pages: publicar de graça

Coloque seu projeto no ar em minutos — gratuito, com HTTPS automático e CDN global. Ative Pages, configure o domínio, entenda cache e confirme que está no ar.

6
Tópicos
~40
Minutos
Intermediário
🌐
Deploy
1

🌐 O que é GitHub Pages

Hospedagem gratuita de sites estáticos direto do seu repositório

🎯 GitHub Pages em 30 segundos

Você tem arquivos HTML/CSS/JS no GitHub. Pages os serve como site público com URL própria, HTTPS automático e CDN global — sem pagar nada, sem configurar servidor.

REPO index.html style.css git push BUILD GitHub Actions CDN HTTPS grátis global 🌍 WEB usuario .github.io 1-10 min Fastly CDN

Push → build automático → CDN → site no ar. Processo todo gerenciado pelo GitHub.

✓ O que Pages inclui (grátis)

  • HTTPS automático (Let's Encrypt)
  • CDN global (Fastly)
  • URL usuario.github.io/repo
  • Deploys automáticos a cada push

✗ O que Pages NÃO faz

  • Backend/servidor (apenas estático)
  • Banco de dados
  • Node.js, PHP, Python em execução
  • Repos privados (plano gratuito)
🆓
Gratuito
Para repos públicos
🔒
HTTPS
Let's Encrypt automático
🌍
CDN global
Fastly ~200 PoPs
📄
Estático
HTML/CSS/JS/imagens
2

⚙️ Ativar Pages

Branch/pasta ou GitHub Actions — 3 caminhos, 1 resultado

A

Via interface web (mais fácil)

  1. Abra o repositório no GitHub
  2. Vá em Settings → Pages
  3. Em "Source", selecione a branch (main) e pasta (/ ou /docs)
  4. Clique Save — aguarde 1-5 minutos
B

Via gh CLI (rápido e automático)

# Ativar Pages na branch main, pasta raiz
$ gh api repos/:owner/:repo/pages \
  -X POST \
  -f source='{"branch":"main","path":"/"}'

# Verificar status
$ gh api repos/:owner/:repo/pages | jq '.status, .html_url'
C

Branch gh-pages dedicada

# Criar e fazer push para branch gh-pages
$ git checkout --orphan gh-pages
$ git rm -rf .              # limpa a branch
$ cp -r dist/* .             # copia os arquivos de build
$ git add . && git commit -m "deploy: v1.0"
$ git push -u origin gh-pages

💡 Qual método usar?

Para projetos simples (HTML/CSS puro): branch main + pasta /. Para projetos com build (React, Vue, etc.): GitHub Actions faz o build e publica automaticamente. A pasta /docs é útil quando código e docs ficam no mesmo repo.

🌿
Branch main
Mais simples
📂
/docs folder
Docs + código juntos
🔀
gh-pages branch
Deploy isolado
⚙️
Actions
Build automático
3

🔗 URL usuario.github.io

Endereço padrão — compartilhe em currículos e portfolios

📁 Project site (mais comum)

Repo: usuario/meu-projeto

URL: usuario.github.io/meu-projeto/

Precisa de index.html na raiz da branch/pasta configurada.

👤 User site (portfólio pessoal)

Repo: usuario/usuario.github.io

URL: usuario.github.io

Apenas 1 por conta. Serve da branch main automaticamente.

VERIFICAR E INSPECIONAR URL
# Ver a URL do Pages do repo atual
$ gh api repos/:owner/:repo/pages --jq '.html_url'

# Abrir no browser diretamente
$ gh browse                     # abre o repositório
$ gh browse --settings          # vai para Settings

# Testar com curl (headless)
$ curl -I https://usuario.github.io/meu-projeto/
# Deve retornar: HTTP/2 200
🏠
index.html
Obrigatório na raiz
🚫
404.html
Página de erro custom
⚠️
Case sensitive
URLs maiúsculas importam
🔚
Trailing slash
/repo/ ≠ /repo
4

🌍 Domínio customizado e CNAME

meusite.com em vez de usuario.github.io — profissionalismo com ~R$50/ano

1

Criar arquivo CNAME no repo

# Na raiz do branch de Pages:
$ echo "meusite.com" > CNAME
$ git add CNAME && git commit -m "chore: adiciona domínio customizado"
$ git push
2

Configurar DNS no registrar

Para www (subdomínio):

Tipo: CNAME
Nome: www
Valor: usuario.github.io

Para apex domain (raiz):

Tipo: A  |  Nome: @  |  Valor: 185.199.108.153
Tipo: A  |  Nome: @  |  Valor: 185.199.109.153
Tipo: A  |  Nome: @  |  Valor: 185.199.110.153
Tipo: A  |  Nome: @  |  Valor: 185.199.111.153
3

Ativar HTTPS e verificar

Em Settings → Pages, configure o domínio e marque "Enforce HTTPS". Aguarde propagação DNS (até 48h).

# Testar propagação DNS
$ dig www.meusite.com CNAME
$ nslookup meusite.com
📄
CNAME file
Domínio no repo
🌐
DNS CNAME
Aponta para GitHub
⏱️
Propagação
Até 48h
🔒
Enforce HTTPS
Redirect automático
5

⏱️ Cache e atualização

Por que o site não atualizou? Entenda e resolva em segundos

🤔 "Fiz push mas o site não mudou!"

Isso acontece por cache em duas camadas: (1) o deploy do GitHub Pages leva 1-10 minutos; (2) seu browser cacheia os arquivos. Aguarde o build completar e faça hard refresh.

✓ Como verificar o build

  • Aba Actions no GitHub: status do workflow "pages build"
  • gh run list --workflow pages no terminal
  • gh run watch monitora em tempo real
  • Settings → Pages mostra "Your site is live at..."

🛠️ Forçar atualização no browser

  • Windows/Linux: Ctrl+Shift+R
  • macOS: Cmd+Shift+R
  • DevTools → Network → Disable cache (enquanto aberto)
  • Adicionar ?v=2 na URL para testar
MONITORAR DEPLOY EM TEMPO REAL
# Ver último run do Pages
$ gh run list --limit 3
STATUS  TITLE                           WORKFLOW       ID
✓       pages build and deployment      pages          123456789
✓       pages build and deployment      pages          123456788

# Assistir deploy em curso
$ gh run watch
Refreshing run status every 3 seconds. Press Ctrl+C to quit.
✓ build (3s)
✓ deploy (8s)
✓ Run pages build and deployment completed with 'success'
1-10 min
Tempo de build
🔄
Hard refresh
Ctrl+Shift+R
👁️
gh run watch
Deploy ao vivo
📦
?v=2
Cache busting
6

✅ Verificar que está no ar

Confirmar o deploy com comandos reais — sem suposições

CHECKLIST DE VERIFICAÇÃO PÓS-DEPLOY
# 1. Confirmar que Pages está ativo
$ gh api repos/:owner/:repo/pages --jq '{status:.status, url:.html_url}'
{"status":"built","url":"https://usuario.github.io/repo/"}

# 2. Testar HTTP status
$ curl -s -o /dev/null -w "%{http_code}" https://usuario.github.io/repo/
200

# 3. Verificar HTTPS redirect
$ curl -I http://usuario.github.io/repo/
HTTP/1.1 301 Moved Permanently
Location: https://usuario.github.io/repo/

# 4. Verificar conteúdo correto
$ curl -s https://usuario.github.io/repo/ | grep ""
<title>Meu Projeto</title></code></pre>
      </div>

      <div class="bg-primary/10 border border-primary/30 rounded-xl p-5 mb-6">
        <h3 class="text-lg font-semibold text-primary mb-2 flex items-center"><span class="mr-2">📊</span> Monitoramento contínuo</h3>
        <p class="text-neutral-300 text-sm">Para projetos em produção, configure o UptimeRobot (gratuito) para pingar o site a cada 5 minutos e te avisar por email se cair. Também adicione o badge de status ao README: <code class="text-rose-300">![Uptime](https://img.shields.io/website?url=...)</code></p>
      </div>

      <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
        <div class="bg-emerald-900/20 rounded-xl border border-emerald-500/30 p-5">
          <h4 class="font-bold text-emerald-400 mb-3">✓ Deploy bem-sucedido</h4>
          <ul class="space-y-2 text-neutral-300 text-sm">
            <li class="flex items-start space-x-2"><span class="text-emerald-400">✓</span><span>Actions mostra círculo verde</span></li>
            <li class="flex items-start space-x-2"><span class="text-emerald-400">✓</span><span>curl retorna HTTP 200</span></li>
            <li class="flex items-start space-x-2"><span class="text-emerald-400">✓</span><span>HTTPS ativo (cadeado no browser)</span></li>
            <li class="flex items-start space-x-2"><span class="text-emerald-400">✓</span><span>Conteúdo mais recente visível</span></li>
          </ul>
        </div>
        <div class="bg-red-900/20 rounded-xl border border-red-500/30 p-5">
          <h4 class="font-bold text-red-400 mb-3">✗ Deploy com problema</h4>
          <ul class="space-y-2 text-neutral-300 text-sm">
            <li class="flex items-start space-x-2"><span class="text-red-400">✗</span><span>Actions mostra X vermelho → veja os logs</span></li>
            <li class="flex items-start space-x-2"><span class="text-red-400">✗</span><span>404 → verifique se index.html existe na pasta certa</span></li>
            <li class="flex items-start space-x-2"><span class="text-red-400">✗</span><span>Site antigo → hard refresh + aguardar CDN</span></li>
            <li class="flex items-start space-x-2"><span class="text-red-400">✗</span><span>HTTPS não disponível → aguarde provisionamento</span></li>
          </ul>
        </div>
      </div>

      <div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
        <div class="bg-dark-800 rounded-lg p-3 border border-dark-600 text-center"><div class="text-rose-400 text-lg mb-1">🟢</div><div class="text-xs font-semibold">HTTP 200</div><div class="text-xs text-neutral-500 mt-1">Site acessível</div></div>
        <div class="bg-dark-800 rounded-lg p-3 border border-dark-600 text-center"><div class="text-rose-400 text-lg mb-1">🔒</div><div class="text-xs font-semibold">HTTPS</div><div class="text-xs text-neutral-500 mt-1">Cadeado no browser</div></div>
        <div class="bg-dark-800 rounded-lg p-3 border border-dark-600 text-center"><div class="text-rose-400 text-lg mb-1">📡</div><div class="text-xs font-semibold">UptimeRobot</div><div class="text-xs text-neutral-500 mt-1">Monitor gratuito</div></div>
        <div class="bg-dark-800 rounded-lg p-3 border border-dark-600 text-center"><div class="text-rose-400 text-lg mb-1">🏷️</div><div class="text-xs font-semibold">Badge uptime</div><div class="text-xs text-neutral-500 mt-1">Status no README</div></div>
      </div>
    </section>

    <!-- RESUMO FINAL -->
    <section class="bg-gradient-to-br from-rose-900/30 via-dark-800 to-dark-900 rounded-2xl border border-rose-500/30 p-8 mb-10">
      <h2 class="text-2xl font-bold mb-6">🎯 Resumo do módulo</h2>
      <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
        <div>
          <h3 class="text-rose-400 font-semibold mb-3">O que você aprendeu</h3>
          <ul class="space-y-2 text-neutral-300 text-sm">
            <li class="flex items-start space-x-2"><span class="text-rose-400">✓</span><span>GitHub Pages: hospedagem gratuita de sites estáticos</span></li>
            <li class="flex items-start space-x-2"><span class="text-rose-400">✓</span><span>3 formas de ativar: branch, /docs, ou gh-pages</span></li>
            <li class="flex items-start space-x-2"><span class="text-rose-400">✓</span><span>URL padrão usuario.github.io/repo</span></li>
            <li class="flex items-start space-x-2"><span class="text-rose-400">✓</span><span>Domínio customizado com CNAME + DNS</span></li>
            <li class="flex items-start space-x-2"><span class="text-rose-400">✓</span><span>Cache: aguardar build + hard refresh</span></li>
            <li class="flex items-start space-x-2"><span class="text-rose-400">✓</span><span>Verificação completa com curl e gh CLI</span></li>
          </ul>
        </div>
        <div>
          <h3 class="text-rose-400 font-semibold mb-3">Próximo módulo</h3>
          <p class="text-neutral-300 text-sm mb-4">Seu projeto está no ar — agora é hora de evoluir, versionar releases, construir portfólio e manter o projeto vivo ao longo do tempo. Fechamento do curso.</p>
          <a href="modulo-6-4.html" class="inline-flex items-center space-x-2 bg-rose-500/20 text-rose-400 hover:bg-rose-500/30 px-5 py-2.5 rounded-lg font-semibold transition-colors text-sm">
            <span>Módulo 6.4 — Evoluir e divulgar</span><span>→</span>
          </a>
        </div>
      </div>
      <div class="flex justify-start space-x-3">
        <a href="modulo-6-2.html" class="px-4 py-2 rounded-lg border border-rose-500/30 text-rose-400 hover:bg-rose-500/10 text-sm font-semibold transition-colors">← Módulo 6.2</a>
        <a href="modulo-6-4.html" class="px-4 py-2 rounded-lg bg-rose-500/20 text-rose-400 hover:bg-rose-500/30 text-sm font-semibold transition-colors">Próximo: Evoluir →</a>
      </div>
    </section>

  </main>

  <footer class="border-t border-dark-600 py-8 mt-8">
    <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 text-center text-neutral-500 text-sm">
      <p>Claude Code — Do Zero ao Projeto · <a href="https://inema.club" target="_blank" class="text-sky-400 hover:text-sky-300">INEMA.CLUB</a></p>
    </div>
  </footer>

  <script>
    const themeToggle = document.getElementById('theme-toggle');
    const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
    const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
    const html = document.documentElement;
    if (localStorage.getItem('theme') === 'light') {
      themeToggleDarkIcon.classList.remove('hidden'); html.classList.remove('dark');
    } else { themeToggleLightIcon.classList.remove('hidden'); }
    themeToggle.addEventListener('click', () => {
      themeToggleDarkIcon.classList.toggle('hidden');
      themeToggleLightIcon.classList.toggle('hidden');
      html.classList.toggle('dark');
      localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
    });
  </script>
</body>
</html>