# 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.

Publicado em 2026-10-25 · https://ikiform.com/pt-br/guides/contact-form-static-site/github-pages

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`:

```bash
curl -i -X POST -d "message=hello" https://ada.github.io/notebook/contact.html
```

`Resposta`:

```http
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 Pages | O 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 personalizados | Uma Content-Security-Policy só pode vir de uma tag `<meta>` na página |
| O repositório muitas vezes é público | Tudo 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édito | Deixe 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 |

Diagrama: O que o GitHub Pages roda, e para onde o envio de um formulário de contato precisa ir

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`:

```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`:

```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](https://ikiform.com/pt-br/docs/guides/jekyll.md) 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](https://ikiform.com/pt-br/docs/guides/hugo.md) 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](https://ikiform.com/blog/form-submissions-to-slack.md) ou no [Discord](https://ikiform.com/blog/form-submissions-to-discord.md).

## 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

| Problema | O que fazer |
| --- | --- |
| `405 Not Allowed` depois de clicar em Enviar | O 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 sua | O 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 spam | O 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.io | Uma Content-Security-Policy via `<meta>` no seu layout não tem `https://ikiform.com` em `form-action` ou `connect-src`. |

## Perguntas

### O GitHub Pages consegue processar envios de formulário?

Não. O GitHub Pages serve arquivos estáticos e não roda nenhum código quando um visitante envia um formulário, então um POST para uma URL do Pages recebe 405 Not Allowed. Aponte o action do formulário para um backend de formulários.

### É seguro fazer commit do endpoint do formulário num repositório público?

Sim. O id do formulário acaba no seu HTML público de qualquer forma. Proteja o formulário com domínios permitidos e as verificações de spam, e nunca faça commit de uma chave de API secreta, porque um site estático não tem um lugar seguro para guardá-la.

### Por que meu redirecionamento de agradecimento vai para a página errada num project site?

Um caminho como /thanks.html aponta para a raiz de username.github.io, não para o seu projeto. Use a URL completa com o caminho do repositório, ou monte-a a partir de document.baseURI no navegador.

### O formulário continua funcionando depois que eu adiciono um domínio personalizado ao GitHub Pages?

Sim. O formulário envia ao Ikiform seja qual for o domínio que serve a página. Adicione o domínio personalizado em Allowed domains ao lado de username.github.io, e atualize qualquer valor de _redirect para usar o domínio personalizado quando o HTTPS estiver ativado.

### O Ikiform é gratuito?

Não, não há plano gratuito. Testar é grátis: você pode enviar 50 envios de teste por mês a partir de localhost antes de pagar. Para entrar no ar, o Pro começa em US$ 6/mês para 500 envios, ou você pode pagar uma vez 10.000 envios por US$ 49 ou 50.000 envios por US$ 149, com créditos que nunca expiram.

## Mais guias

- [Formulário de contato em site estático](https://ikiform.com/pt-br/guides/contact-form-static-site.md): Coloque um formulário de contato funcionando num site estático sem servidor: para onde vai o POST, spam, respostas por e-mail e código para todo host e framework.
- [Netlify](https://ikiform.com/pt-br/guides/contact-form-static-site/netlify.md): Rode um formulário de contato num site da Netlify sem o Netlify Forms: sem detecção no build, envios JSON a partir do React e um formulário separado para deploy previews.
- [Cloudflare Pages](https://ikiform.com/pt-br/guides/contact-form-static-site/cloudflare-pages.md): Adicione um formulário de contato ao Cloudflare Pages sem escrever uma Function: envie para um backend de formulários, defina headers no _headers e use suas próprias chaves do Turnstile.
- [Site de portfólio](https://ikiform.com/pt-br/guides/contact-form-static-site/portfolio-website.md): Um formulário de contato para portfólio que faz as perguntas de uma primeira conversa: projeto, orçamento, prazo e um link, sem servidor e com respostas pela sua caixa de entrada.
- [Vercel](https://ikiform.com/pt-br/guides/contact-form-static-site/vercel.md): Uma página estática na Vercel responde a um POST de formulário com 405 Method Not Allowed; veja por quê e como enviar o formulário para um backend.
- [Amazon S3](https://ikiform.com/pt-br/guides/contact-form-static-site/aws-s3.md): Endpoints de site do S3 só aceitam GET e HEAD via HTTP simples, então envie o formulário de contato para um backend de formulários e acerte os redirecionamentos com o CloudFront.
