Formulário de contato em site estático

Formulário de contato em site estático7 min de leitura

Adicione um formulário de contato a um site no GitHub Pages

O GitHub Pages não roda código de servidor, então um POST de formulário recebe 405 Not Allowed; envie para um backend de formulários e acerte os redirecionamentos de project sites.

Preet SutharFundador, Ikiform

O GitHub Pages só serve arquivos estáticos, então um formulário que envia para o seu próprio site recebe 405 Not Allowed e a mensagem se perde. Defina o action do formulário como um endpoint do Ikiform, adicione username.github.io e qualquer domínio personalizado em Allowed domains e redirecione para uma página de agradecimento que inclua o caminho do seu project site. Nada muda no seu repositório nem nas configurações do Pages, e nenhum segredo vai parar no repositório público.

O que muda no GitHub Pages

O GitHub Pages publica o que o seu repositório ou o seu workflow do Actions gera, e nada além disso. O Jekyll roda no build se você o usa, e o resultado são arquivos. Não existe handler de requisições, então o mail() do PHP, um script Node ou um formulário CGI em Python não vão rodar, faça o commit que fizer.

Dá para ver a falha pelo terminal. Um POST para qualquer página de um site no Pages recebe isto:

Terminal
curl -i -X POST -d "message=hello" https://ada.github.io/notebook/contact.html
Resposta
HTTP/2 405
server: Varnish
content-length: 131

<html>
<head><title>405 Not Allowed</title></head>

Mais quatro coisas diferenciam o Pages de outros hosts estáticos, e cada uma afeta o formulário de contato:

Característica do PagesO que significa para o formulário
Project sites ficam sob um caminho, username.github.io/repo/Uma URL de agradecimento /thanks.html aponta para o user site, não para o seu projeto. Toda URL precisa da parte /repo/
Nenhuma configuração para headers de resposta personalizadosUma Content-Security-Policy só pode vir de uma tag <meta> na página
O repositório muitas vezes é públicoTudo nele é público. O id do formulário pode ficar lá; uma chave de API secreta ou uma senha de e-mail, nunca
O GitHub diz que sites do Pages não devem ser usados para transações sensíveis, como enviar senhas ou números de cartão de créditoDeixe o formulário de contato ser só um formulário de contato. Não colete dados de cartão nem senhas de conta por ele
O que o GitHub Pages roda, e para onde o envio de um formulário de contato precisa iro que o GitHub Pages rodaonde o envio do visitante chegagit pushpara a branch do PagesbuildJekyll ou um workflowdo Actionsarquivosestáticosemusername.github.io/repocontact.htmlservido pelo PagesIkiformarmazena, avisa vocêrepo/thanks.htmlservido pelo Pages405 Not Allowednada armazenadodeployPOST303action="/contact"O que o GitHub Pages roda, e para onde o envio de um formulário de contato precisa iro que o GitHub Pages rodaonde o envio do visitante chegagit pushpara a branch doPagesbuildJekyll ou umworkflow do Actionsarquivosestáticosemusername.github.io/repocontact.htmlservido pelo PagesIkiformarmazena, avisa vocêrepo/thanks.htmlservido pelo Pages405 NotAllowednadaarmazenadodeployPOST303action="/contact"
  1. Você faz push para a branch de onde o Pages publica.
  2. O Jekyll ou o seu workflow do Actions gera o site uma vez.
  3. O Pages serve o resultado como arquivos estáticos no github.io ou no seu domínio personalizado.
  4. Um visitante envia o formulário de contato. Se o action dele aponta para o seu próprio site, o Pages responde 405 Not Allowed.
  5. Com o action definido como o seu endpoint do Ikiform, o Ikiform armazena a mensagem e manda um e-mail para você.
  6. O Ikiform redireciona o navegador para a página de agradecimento, que o Pages serve como qualquer outro arquivo.
O código roda no GitHub uma vez por push. Nada seu roda quando um visitante clica em Enviar, e é por isso que o envio precisa sair do github.io.

O formulário

Para um site escrito à mão, adicione contact.html ao lado do index.html. Este é para um project site em ada.github.io/notebook, então o redirecionamento inclui /notebook/:

contact.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta
      http-equiv="Content-Security-Policy"
      content="default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"
    />
    <title>Contact</title>
  </head>
  <body>
    <h1>Get in touch</h1>
    <form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
      <label for="name">Name</label>
      <input id="name" name="name" autocomplete="name" required />

      <label for="email">Email</label>
      <input id="email" name="email" type="email" autocomplete="email" required />

      <label for="about">What is it about?</label>
      <select id="about" name="about">
        <option value="question">A question about the project</option>
        <option value="hire">Work together</option>
        <option value="other">Something else</option>
      </select>

      <label for="message">Message</label>
      <textarea id="message" name="message" rows="6" minlength="10" required></textarea>

      <input type="hidden" name="page" value="docs-home" />
      <input type="hidden" name="_redirect" value="https://ada.github.io/notebook/thanks.html" />
      <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
      <button type="submit">Send</button>
    </form>
  </body>
</html>
  • A Content-Security-Policy via <meta> é opcional. Mantenha-a se você já tem uma, e garanta que form-action e connect-src incluam https://ikiform.com. O Pages não consegue enviar o header por você.
  • O select about diz por que alguém escreveu antes de você abrir a mensagem. Troque as opções pelos assuntos sobre os quais as pessoas realmente escrevem para você.
  • O input oculto page registra em que página o formulário estava, o que é útil quando o mesmo formulário aparece em várias páginas da documentação.
  • Mantenha email como nome do campo. Os e-mails de notificação o usam como endereço de reply-to.

Se o mesmo HTML é publicado em vários caminhos, ou se você não tem certeza de qual será o caminho final, monte o redirecionamento no navegador a partir do próprio endereço da página. Coloque isto depois do formulário:

contact.html
<script>
  document.querySelectorAll('input[name="_redirect"]').forEach((input) => {
    input.value = new URL("thanks.html", document.baseURI).href;
  });
</script>

Usa Jekyll? Não escreva a URL fixa. O guia do Jekyll tem um include que a monta com absolute_url, que adiciona o seu baseurl para project sites. Gera com Astro, Vite ou Hugo por um workflow do Actions? Defina o base path do framework como /repo/ para os assets do formulário carregarem, e use o guia do Hugo ou do framework para o formulário em si.

Domínios permitidos no github.io

Adicione ada.github.io, com o seu próprio nome de usuário. Ele cobre o seu user site e todos os project sites, porque os project sites compartilham o mesmo host. Adicione também o seu domínio personalizado, se usar um; example.com também cobre www.example.com.

Nunca adicione github.io sozinho. Todo site do Pages passaria a corresponder, inclusive cópias do seu formulário em sites de outras pessoas. Com domínios permitidos definidos, um _redirect para https://ada.github.io/notebook/thanks.html é seguido; sem eles, o Ikiform o ignora e mostra a página de agradecimento hospedada.

Ative Enforce HTTPS nas configurações do Pages do repositório. O Ikiform só segue valores de _redirect que começam com https:, e um domínio personalizado ainda sem certificado serve páginas http:.

Substituindo um link mailto:

Muitos sites no Pages começam com um link mailto: no rodapé. Trocá-lo pelo formulário impede que novos scrapers leiam seu endereço na página, e visitantes sem app de e-mail configurado finalmente conseguem falar com você. Duas coisas a saber antes de apagá-lo:

  • O endereço continua no histórico do seu repositório e em qualquer fork ou arquivo da página antiga. O formulário ajuda daqui para frente; ele não torna privado de novo um endereço que ficou público por anos.
  • As respostas saem da sua própria caixa de entrada. Os e-mails de notificação definem o remetente como endereço de reply-to, então clicar em Reply funciona como o link mailto: funcionava, só que do outro lado.

O Ikiform nunca envia e-mail ao visitante, então diga na página de agradecimento quando ele pode esperar uma resposta. Se você recebe mais mensagens do que consegue responder por e-mail, um webhook pode jogar cada uma no Slack ou no Discord.

Confira se funciona

Faça isto no endereço github.io publicado. O Pages não tem deploys de preview, então teste antes localmente com um servidor local.

  1. Adicione localhost e username.github.io em Allowed domains, mais o seu domínio personalizado, se tiver um.
  2. Faça commit de contact.html e thanks.html, espere o deploy do Pages terminar na aba Actions do repositório e abra a página publicada.
  3. Envie o formulário. Você deve cair no seu próprio thanks.html, com o caminho do projeto na barra de endereço.
  4. Abra Submissions no painel. O novo envio está lá com todos os campos que você enviou.
  5. Confira a caixa de entrada de cada destinatário das notificações. Cada um recebe um e-mail com os campos, e Reply vai para quem enviou quando o formulário tem um campo email.
  6. Não chegou nada? Mude o filtro de status do painel para Spam. Spam recebe a mesma resposta de sucesso de um envio real.

Pegadinhas

ProblemaO que fazer
405 Not Allowed depois de clicar em EnviarO formulário publicado ainda envia para o seu próprio site. Veja o código-fonte da página no github.io e confira o action; um build antigo em cache pode continuar no ar por alguns minutos.
A página de agradecimento do Ikiform em vez da suaO valor de _redirect está sem o caminho do repositório, é http:, ou o formulário não tem domínios permitidos. Adicione username.github.io e use a URL https: completa.
403 origin_not_allowed, ou todo envio cai no spamO domínio da página não está em Allowed domains do formulário. Adicione-o, mais localhost para testes locais. Subdomínios de um domínio listado são permitidos.
Os envios funcionam localmente, mas o console mostra um erro de CSP no github.ioUma Content-Security-Policy via <meta> no seu layout não tem https://ikiform.com em form-action ou connect-src.

Perguntas

Travou em algum passo ou quer um guia para o seu caso? Envie um e-mail para hello@ikiform.com.