# Adicione um formulário de contato a um site estático no Amazon S3

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

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

Um endpoint de site do S3 responde a um `POST` de formulário com `405 MethodNotAllowed`, então aponte o `action` do formulário para o seu endpoint do Ikiform. Como o endpoint é `http:` simples, um `_redirect` de volta para ele nunca é seguido; defina **Redirect after submit** nas configurações do formulário, ou coloque o CloudFront na frente para ter `https:`. Com o CloudFront na frente do endpoint REST do bucket, redirecione para um nome de arquivo completo, como `thanks.html`, porque o default root object só vale para a raiz.

## O que muda no S3

Um bucket configurado para hospedagem de site estático ganha o próprio endpoint de site, como `http://www.example.com.s3-website-us-east-1.amazonaws.com`. A AWS documenta que ele só suporta requisições `GET` e `HEAD` em objetos. Um envio de formulário recebe uma página de erro em HTML:

`Resposta a um POST`:

```html
<html>
<head><title>405 Method Not Allowed</title></head>
<body>
<h1>405 Method Not Allowed</h1>
<ul>
<li>Code: MethodNotAllowed</li>
<li>Message: The specified method is not allowed against this resource.</li>
<li>Method: POST</li>
<li>ResourceType: OBJECT</li>
```

Mais três características do S3 influenciam o formulário de contato:

| Característica do S3 | O que significa para o formulário |
| --- | --- |
| Endpoints de site não suportam HTTPS | O Ikiform só segue um `_redirect` para uma URL `https:`, então um redirecionamento de volta para o endpoint puro é ignorado |
| Os headers de resposta vêm do CloudFront, não do bucket | Uma Content-Security-Policy é definida numa response headers policy do CloudFront |
| O default root object do CloudFront só vale para a raiz | Com uma origem REST, `https://example.com/thanks/` não carrega `thanks/index.html`; redirecione para `thanks.html` |

Para um site `https:`, a AWS recomenda o Amplify Hosting, que roda no CloudFront, ou o CloudFront na frente do bucket. O formulário funciona dos dois jeitos. O que muda é onde o visitante vai parar depois de enviar.

Diagrama: Onde o visitante cai depois de enviar, com e sem CloudFront

1. No endpoint de site puro, a página é servida via http e envia ao Ikiform.
2. O Ikiform armazena a mensagem, mas ignora um _redirect para um endereço http e mostra a página de agradecimento hospedada.
3. Definir Redirect after submit no painel é o jeito de contornar isso, porque essa configuração aceita URLs http.
4. Atrás do CloudFront com o seu próprio domínio, a página é https.
5. O Ikiform segue o _redirect para o seu thanks.html, desde que o domínio esteja em Allowed domains.

A mensagem é armazenada de qualquer jeito. Só o último passo, para onde o navegador vai, depende de https.

## Qual origem do CloudFront

O CloudFront pode ler do bucket de duas formas, e a escolha decide como deve ser a sua URL de agradecimento:

| Origem | Bucket | Páginas index de pastas | URL de agradecimento |
| --- | --- | --- | --- |
| O endpoint de site do S3, como origem personalizada | Precisa ter leitura pública | Funcionam: `/thanks/` carrega `thanks/index.html` | `/thanks/` ou `/thanks.html` |
| O endpoint REST, com origin access control | Continua privado, lido só pelo CloudFront | Só na raiz, pelo default root object | `/thanks.html`, um nome de arquivo completo |

Um nome de arquivo completo funciona com as duas, e é por isso que os exemplos usam um. Se você prefere não administrar o CloudFront, o Amplify Hosting pode fazer o deploy a partir do bucket com `https:` e uma CDN gerenciada; o formulário também não precisa de mudança lá.

## O formulário

Sites no S3 costumam ser sites de empresas, então este formulário pede um e-mail de trabalho e o nome da empresa. Envie-o como `contact.html` ao lado de `thanks.html`:

`contact.html`:

```html
<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">Work email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="company">Company</label>
  <input id="company" name="company" autocomplete="organization" />

  <label for="message">How can we help?</label>
  <textarea id="message" name="message" rows="6" minlength="10" required></textarea>

  <input type="hidden" name="_redirect" value="https://www.example.com/thanks.html" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button type="submit">Send</button>
</form>
```

- O `_redirect` aponta para o domínio do CloudFront, com um nome de arquivo. Ele só é seguido quando `www.example.com` ou `example.com` está em **Allowed domains**.
- No endpoint `http:` puro, remova o input `_redirect` e defina **Redirect after submit** com a página de agradecimento. Essa configuração aceita URLs `http:`, porque só a sua equipe pode mudá-la.
- Mantenha `email` como nome do campo. Os e-mails de notificação o usam como endereço de reply-to.

Num site de empresa, geralmente mais de uma pessoa lê a caixa de entrada de contato. Adicione até 10 destinatários de notificação confirmados ao formulário; cada um recebe a mensagem, e quem clicar em **Reply** responde direto ao remetente. Se as mensagens também devem chegar a um canal compartilhado ou a uma fila de tickets, adicione um webhook assinado e direcione-o pelo [Zapier](https://ikiform.com/blog/form-submissions-to-zapier.md) ou pelo seu próprio código. O campo `company` chega como texto simples, então já está pronto para filtrar em qualquer um dos dois lugares.

Antes de subir, teste a página a partir de um servidor local com `localhost` em **Allowed domains**. Envios de `localhost` contam como testes, então você pode acertar o formulário antes de o bucket servi-lo.

Sincronize a pasta com o bucket e invalide as páginas no CloudFront para o novo formulário ser servido na hora:

`Terminal`:

```bash
aws s3 sync ./site s3://www.example.com --delete

aws cloudfront create-invalidation --distribution-id E1EXAMPLE --paths "/contact.html" "/thanks.html"
```

## Domínios permitidos em hosts da AWS

| Onde a página é servida | Adicione |
| --- | --- |
| Endpoint de site puro | O host completo do endpoint do bucket, como `www.example.com.s3-website-us-east-1.amazonaws.com` |
| Domínio padrão do CloudFront | O host exato `d111111abcdef8.cloudfront.net`, enquanto você testa |
| Seu próprio domínio | `example.com`, que também cobre `www.example.com` |

Nunca adicione `amazonaws.com`, um sufixo regional `s3-website` ou `cloudfront.net` sozinhos. Todo bucket ou distribuição do mundo passaria a corresponder. A AWS registra os domínios de endpoint de site do S3 na Public Suffix List pelo mesmo motivo: cada bucket é um site separado.

## Headers pelo CloudFront

Os metadados de objetos do S3 não conseguem definir uma Content-Security-Policy, então defina-a numa response headers policy do CloudFront e anexe-a ao default cache behavior da distribuição. O valor do header é limitado a 1.783 caracteres:

`csp-policy.json`:

```json
{
  "Name": "site-csp",
  "SecurityHeadersConfig": {
    "ContentSecurityPolicy": {
      "Override": true,
      "ContentSecurityPolicy": "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"
    }
  }
}
```

`Terminal`:

```bash
aws cloudfront create-response-headers-policy --response-headers-policy-config file://csp-policy.json
```

> `Override: true` substitui qualquer CSP que a origem envie. Com uma origem S3 pura não existe nenhuma, mas isso mantém a policy no controle se você trocar de origem depois.

## Confira se funciona

1. Adicione os domínios da tabela acima, mais `localhost` enquanto testa.
2. Envie a partir do seu domínio do CloudFront. Você deve cair em `thanks.html` no seu próprio domínio.
3. Se você também mantém o endpoint de site puro público, envie a partir dele e confirme que chega à página definida em **Redirect after submit**.
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 Method Not Allowed` com `Code: MethodNotAllowed` | O formulário enviou para o bucket. O CloudFront ainda pode estar servindo o `contact.html` antigo; invalide-o. |
| `AccessDenied` ou `403` depois do redirecionamento | O redirecionamento aponta para uma pasta como `/thanks/` numa distribuição do CloudFront com origem REST. Aponte para `thanks.html`. |
| O navegador cai na página de agradecimento do Ikiform em vez da sua | O `_redirect` é ignorado, a menos que o domínio dele esteja em **Allowed domains** e ele seja uma URL `https:`. Adicione o domínio, ou defina **Redirect after submit** nas configurações do formulário. |
| `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. |

## Perguntas

### Um site estático no S3 consegue tratar envios de formulário?

Não. Endpoints de site do S3 só suportam requisições GET e HEAD, então um POST recebe 405 MethodNotAllowed. Aponte o formulário para um backend de formulários, ou construa uma API com Lambda.

### Preciso do CloudFront para o formulário de contato funcionar?

Não. O formulário também envia ao Ikiform a partir de uma página http. O CloudFront dá https, o que permite ao Ikiform seguir um _redirect de volta para a sua própria página de agradecimento.

### Por que não API Gateway, Lambda e SES?

Funciona e mantém tudo na AWS, mas você mesmo constrói a validação, o filtro de spam, o armazenamento e as novas tentativas, e o SES exige um remetente verificado. Um backend de formulários dá menos manutenção para um formulário de contato.

### O CloudFront vai manter em cache o formulário antigo depois que eu mudá-lo?

Pode manter, até a cópia em cache expirar. Invalide o contact.html depois de cada mudança, ou suba o HTML com um Cache-Control max-age curto para que os novos formulários entrem no ar rápido.

### 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.
- [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.
- [GitHub Pages](https://ikiform.com/pt-br/guides/contact-form-static-site/github-pages.md): 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.
- [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.
- [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.
- [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.
