Formulário de contato em site estático

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

Corrija o erro 405 num formulário de contato hospedado na Vercel

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.

Preet SutharFundador, Ikiform

A Vercel serve o seu build estático como arquivos, e um POST para um arquivo recebe 405 com corpo vazio porque nenhuma função trata esse caminho. Aponte o action do formulário para o seu endpoint do Ikiform, ou escreva uma Vercel Function que aceite o envio. Com o Ikiform, defina o id do formulário por ambiente da Vercel, para que os previews enviem a um formulário de teste e a produção ao formulário real.

Por que a Vercel responde 405

A Vercel direciona cada requisição a um arquivo estático ou a uma função. Um site estático, um build do Vite ou um app Next.js com output: "export" só tem arquivos. Um GET para /contact devolve o arquivo; um POST para o mesmo caminho não tem para onde ir, então a Vercel o recusa antes que qualquer código seu possa rodar:

Terminal
curl -i -X POST -d "message=hello" https://your-project.vercel.app/contact
Resposta
HTTP/2 405
server: Vercel
content-disposition: inline
x-vercel-cache: HIT
content-length: 0

A resposta não tem corpo, então o navegador mostra uma página em branco ou a própria tela de erro. Três configurações levam a isso:

Seu códigoPor que dá 405 na Vercel
<form method="post"> sem actionO formulário envia para a própria página em que está, que é um arquivo estático
action="/api/contact" num projeto Vite ou de HTML puroNão há arquivo numa pasta api/ na raiz do projeto, então não existe função para esse caminho
Uma Server Action ou um Route Handler do Next.js num export estáticooutput: "export" não suporta Server Actions nem Route Handlers que leem a requisição. O next dev acusa o erro, e o build não tem servidor para rodá-los
Como a Vercel decide o que fazer com um POST de formulárioaction aponta para o seu site na Vercelaction aponta para o IkiformPOST/contactdo formulárioroteamentoda Vercelarquivo oufunção?funçãoaqui?sua funçãose vocêescreveu uma405, corpovazioum arquivoestáticoPOST paraikiform.commesmoformulárioIkiformarmazena, avisavocêpágina deagradecimentoum arquivoestático naVercelsimnão303Como a Vercel decide o que fazer com um POST de formulárioaction aponta para o seu site na Vercelaction aponta para o IkiformPOST /contactdo formulárioroteamento daVercelarquivo ou função?funçãoaqui?sua funçãose você escreveu uma405,corpovazioum arquivoestáticoPOST paraikiform.commesmo formulárioIkiformarmazena, avisa vocêpágina deagradecimentoum arquivo estáticona Vercelsim303não
  1. O formulário envia para um caminho no seu deploy da Vercel.
  2. A Vercel procura uma função que trate esse caminho.
  3. Se você escreveu uma, ela roda. Num build estático não existe nenhuma, então a Vercel responde 405 com corpo vazio.
  4. Com o action apontando para o Ikiform, o envio nunca chega à Vercel.
  5. O Ikiform armazena a mensagem, manda um e-mail para você e redireciona o navegador de volta para uma página estática no seu site da Vercel.
A Vercel não está com defeito aqui. Um deploy estático não tem código para um POST, e uma função é o único jeito de adicionar um do lado da Vercel.

Então ou você adiciona uma função ou envia o post para outro lugar. Uma Vercel Function em api/contact.ts funciona, mas aí você precisa de uma API de e-mail, filtro de spam e limites de taxa, e a manutenção é sua. Enviar o post ao Ikiform mantém o deploy estático. A correção no HTML é um atributo:

Antes
<form method="post">
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button>Send</button>
</form>
Depois
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">

Se você prefere escrever a função

Uma função é uma escolha justa quando a mensagem precisa ir para um lugar que só o seu código alcança. Some o que ela exige antes de começar:

  • Uma conta num provedor de e-mail, um domínio de envio verificado e uma chave de API nas variáveis de ambiente da Vercel.
  • Validação no servidor, porque os atributos required do navegador são uma conveniência, não uma verificação.
  • Filtro de spam e limites de taxa, ou um formulário de contato que manda para você todos os bots da internet.
  • Um lugar para guardar as mensagens quando o provedor de e-mail estiver fora do ar, e um jeito de reenviá-las.

Um backend de formulários cobre essa lista por você. Se ainda quiser seu próprio código no meio, deixe o formulário enviando ao Ikiform e receba um webhook assinado numa função; seu próprio servidor mostra o handler.

Um id de formulário por ambiente da Vercel

Todo pull request na Vercel ganha um deploy de preview. Se os previews enviam para o seu formulário de produção, toda mensagem de teste cai na sua caixa de entrada junto com as reais. Crie um segundo formulário no Ikiform para os previews e deixe as variáveis de ambiente da Vercel escolherem. Em Settings › Environment Variables, adicione o mesmo nome duas vezes:

Variáveis de ambiente da Vercel
VITE_IKIFORM_FORM_ID   Production   frm_xxxxxxxxxxxxxxxx
VITE_IKIFORM_FORM_ID   Preview      frm_yyyyyyyyyyyyyyyy

O Vite substitui os placeholders %NAME% no index.html por variáveis VITE_ no build, então um formulário HTML simples pode usá-las sem JavaScript. VITE_VERCEL_ENV é definida pela Vercel em projetos Vite e registra de qual deploy veio uma mensagem:

index.html
<form id="contact" action="https://ikiform.com/f/%VITE_IKIFORM_FORM_ID%" 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="message">Message</label>
  <textarea id="message" name="message" rows="6" minlength="10" required></textarea>

  <input type="hidden" name="build" value="%VITE_VERCEL_ENV%" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button type="submit">Send message</button>
</form>
  • A Vercel lê as variáveis de ambiente no build de um site estático. Depois de mudar uma, faça o deploy de novo, ou o valor antigo continua no HTML.
  • O Vite deixa o placeholder como está quando a variável não existe, então um id ausente vai para produção como %VITE_IKIFORM_FORM_ID% e todo envio falha com 400 Bad Request antes de chegar ao formulário. A verificação abaixo pega isso.
  • O id do formulário é público de qualquer jeito. Só variáveis VITE_ chegam ao navegador, e é por isso que nenhum segredo deve ficar numa delas.
src/contact.js
const form = document.querySelector("#contact");

if (form && form.action.includes("%VITE_")) {
  console.error("VITE_IKIFORM_FORM_ID is not set for this Vercel environment");
  form.querySelector("button").disabled = true;
}

Usa Next.js com output: "export"? Leia process.env.NEXT_PUBLIC_IKIFORM_FORM_ID num client component; o guia principal tem um formulário para export estático. Se o seu app Next.js roda nos servidores da Vercel, uma Server Action pode enviar com uma chave secreta: veja o guia de Next.js.

Headers e URLs limpas

Se o seu site define uma Content-Security-Policy, defina-a no vercel.json e inclua o Ikiform. cleanUrls serve thanks.html em /thanks, o que dá um destino de redirecionamento mais limpo:

vercel.json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "cleanUrls": true,
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"
        }
      ]
    }
  ]
}

Previews e domínios permitidos

URLs de preview como your-project-git-branch-team.vercel.app não são subdomínios de your-project.vercel.app, então não correspondem a ele em Allowed domains. Os envios a partir delas caem no spam com origin_mismatch. É mais um motivo para ter um formulário de preview separado: deixe os domínios permitidos dele vazios, ou adicione o host de preview que você está testando.

Com o Vercel Authentication ligado e o escopo Standard Protection, que cobre todos os deploys exceto os domínios de produção, as URLs de preview ficam atrás de um login da Vercel. Só a sua equipe consegue acessá-las, então um formulário de preview sem domínios permitidos não fica exposto ao público. Confira Settings › Deployment Protection antes de contar com isso. Envios a partir de previews não são testes de localhost, então contam no seu plano como qualquer envio real.

Confira se funciona

  1. Rode o curl acima na sua URL de produção antes da correção e confirme o 405. Depois da correção, o action do formulário no HTML publicado começa com https://ikiform.com/f/.
  2. Abra um deploy de preview, envie e confirme que a mensagem chega no formulário de preview, não no de produção.
  3. Envie no seu domínio de produção e confira se o campo build diz production.
  4. 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.
  5. 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
Ainda dá 405 depois de mudar o formulárioO deploy que você está testando foi gerado antes da mudança, ou um handler JavaScript envia para o seu próprio caminho com fetch. Procure /api/contact nos arquivos gerados.
400 Bad Request em todo envio, ou 404 not_found do IkiformO id do formulário não chegou ao build, ou é o id errado para aquele ambiente. Confira se a variável existe para aquele ambiente e faça o deploy de novo.
Os previews funcionam, mas os envios em produção caem no spamSeu domínio de produção não está em Allowed domains do formulário de produção. Adicione o domínio personalizado, além de your-project.vercel.app.
O navegador cai na página de agradecimento do Ikiform em vez da suaO _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.

Perguntas

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