Kontaktformular auf einer statischen Site6 Min. Lesezeit
Den 405-Fehler bei einem auf Vercel gehosteten Kontaktformular beheben
Eine statische Seite auf Vercel beantwortet einen Formular-POST mit 405 Method Not Allowed; hier steht, warum, und wie du das Formular stattdessen an ein Backend sendest.
Vercel liefert deinen statischen Build als Dateien aus, und ein POST an eine Datei bekommt 405 mit leerem Body, weil keine Funktion diesen Pfad verarbeitet. Richte die action des Formulars stattdessen auf deinen Ikiform-Endpunkt aus oder schreib eine Vercel Function, die den Post annimmt. Mit Ikiform setzt du die Formular-ID pro Vercel-Umgebung, damit Previews an ein Testformular und die Produktion an das echte senden.
Warum Vercel mit 405 antwortet
Vercel leitet jeden Request an eine statische Datei oder an eine Funktion. Eine statische Site, ein Vite-Build oder eine Next.js-App mit output: "export" hat nur Dateien. Ein GET auf /contact liefert die Datei; ein POST an denselben Pfad hat kein Ziel, also lehnt Vercel ihn ab, bevor irgendein Code von dir laufen könnte:
curl -i -X POST -d "message=hello" https://your-project.vercel.app/contactHTTP/2 405
server: Vercel
content-disposition: inline
x-vercel-cache: HIT
content-length: 0Die Antwort hat keinen Body, daher zeigt der Browser eine leere Seite oder seine eigene Fehleransicht. Drei Setups führen hierher:
| Dein Code | Warum es auf Vercel 405 gibt |
|---|---|
<form method="post"> ohne action | Das Formular sendet an die Seite, auf der es steht, und die ist eine statische Datei |
action="/api/contact" in einem Vite- oder einfachen HTML-Projekt | Im Projektstamm gibt es keine Datei in einem api/-Ordner, also existiert für diesen Pfad keine Funktion |
| Eine Next.js-Server-Action oder ein Route Handler in einem statischen Export | output: "export" unterstützt keine Server Actions und keine Route Handler, die den Request lesen. next dev meldet den Fehler, und der Build hat keinen Server, der sie ausführen könnte |
- Das Formular sendet an einen Pfad in deinem Vercel-Deployment.
- Vercel sucht eine Funktion, die diesen Pfad verarbeitet.
- Wenn du eine geschrieben hast, läuft sie. In einem statischen Build gibt es keine, also antwortet Vercel mit 405 und leerem Body.
- Zeigt die action auf Ikiform, erreicht der Post Vercel gar nicht.
- Ikiform speichert die Nachricht, schickt dir eine E-Mail und leitet den Browser zurück auf eine statische Seite deiner Vercel-Site.
Du fügst also entweder eine Funktion hinzu oder schickst den Post woandershin. Eine Vercel Function in api/contact.ts funktioniert, aber dann brauchst du eine E-Mail-API, Spamfilter und Rate-Limits, und du pflegst sie. Sendest du den Post an Ikiform, bleibt das Deployment statisch. Die Lösung im HTML ist ein einziges Attribut:
<form method="post">
<input name="email" type="email" required />
<textarea name="message" required></textarea>
<button>Send</button>
</form><form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">Wenn du die Funktion lieber selbst schreibst
Eine Funktion ist eine faire Wahl, wenn die Nachricht irgendwohin muss, wo nur dein Code hinkommt. Rechne vorher zusammen, was dazugehört:
- Ein Konto bei einem E-Mail-Anbieter, eine verifizierte Absenderdomain und ein API-Schlüssel in den Umgebungsvariablen von Vercel.
- Validierung auf dem Server, weil die
required-Attribute des Browsers eine Bequemlichkeit sind, keine Prüfung. - Spamfilter und Rate-Limits, sonst schickt dir das Kontaktformular jeden Bot im Internet.
- Einen Ort, an dem Nachrichten liegen bleiben, wenn der E-Mail-Anbieter ausfällt, und eine Möglichkeit, sie erneut zu senden.
Ein Formular-Backend deckt diese Liste für dich ab. Wenn du trotzdem eigenen Code dazwischen haben willst, lass das Formular weiter an Ikiform senden und empfange einen signierten Webhook in einer Funktion; dein eigener Server zeigt den Handler.
Eine Formular-ID pro Vercel-Umgebung
Jeder Pull Request auf Vercel bekommt ein Preview-Deployment. Wenn Previews an dein Produktionsformular senden, landet jede Testnachricht neben echten in deinem Postfach. Leg ein zweites Ikiform-Formular für Previews an und lass die Umgebungsvariablen von Vercel entscheiden. Füge unter Settings › Environment Variables denselben Namen zweimal hinzu:
VITE_IKIFORM_FORM_ID Production frm_xxxxxxxxxxxxxxxx
VITE_IKIFORM_FORM_ID Preview frm_yyyyyyyyyyyyyyyyVite ersetzt %NAME%-Platzhalter in index.html zur Build-Zeit durch VITE_-Variablen, sodass ein einfaches HTML-Formular sie ohne JavaScript nutzen kann. VITE_VERCEL_ENV setzt Vercel für Vite-Projekte, und es hält fest, aus welchem Deployment eine Nachricht kam:
<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>- Vercel liest Umgebungsvariablen bei einer statischen Site zur Build-Zeit. Nach einer Änderung musst du neu deployen, sonst bleibt der alte Wert im HTML.
- Vite lässt einen Platzhalter stehen, wenn die Variable nicht existiert, daher wird eine fehlende ID als
%VITE_IKIFORM_FORM_ID%ausgeliefert, und jeder Post scheitert mit400 Bad Request, bevor er das Formular erreicht. Die Prüfung unten fängt das ab. - Die Formular-ID ist so oder so öffentlich. Nur
VITE_-Variablen erreichen den Browser, deshalb gehört kein Geheimnis in eine davon.
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;
}Du nutzt Next.js mit output: "export"? Lies stattdessen process.env.NEXT_PUBLIC_IKIFORM_FORM_ID in einer Client-Komponente; die Hauptanleitung hat ein Formular für den statischen Export. Wenn deine Next.js-App doch auf den Servern von Vercel läuft, kann eine Server Action mit einem Secret Key senden: siehe die Next.js-Anleitung.
Header und saubere URLs
Wenn deine Site eine Content-Security-Policy setzt, setze sie in vercel.json und nimm Ikiform auf. cleanUrls liefert thanks.html unter /thanks aus, was ein aufgeräumteres Weiterleitungsziel ergibt:
{
"$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 und erlaubte Domains
Preview-URLs wie your-project-git-branch-team.vercel.app sind keine Subdomains von your-project.vercel.app, passen also unter Allowed domains nicht dazu. Posts von dort landen mit origin_mismatch im Spam. Ein weiterer Grund für ein separates Preview-Formular: Lass seine erlaubten Domains leer oder füge den Preview-Host hinzu, den du gerade testest.
localhost-Tests, sie zählen also wie jede Live-Einsendung auf deinen Tarif.Prüfen, ob es funktioniert
- Führe das
curloben vor der Lösung gegen deine Produktions-URL aus und bestätige den405. Nach der Lösung beginnt dieactiondes Formulars im deployten HTML mithttps://ikiform.com/f/. - Öffne ein Preview-Deployment, sende ab und prüfe, ob die Nachricht im Preview-Formular ankommt, nicht im Produktionsformular.
- Sende auf deiner Produktionsdomain ab und prüfe, ob das Feld
buildden Wertproductionhat. - Prüfe das Postfach jedes Benachrichtigungsempfängers. Jeder bekommt eine E-Mail mit den Feldern, und Reply geht an den Absender, wenn das Formular ein
email-Feld hat. - Nichts angekommen? Stell den Statusfilter im Dashboard auf Spam. Spam bekommt dieselbe Erfolgsantwort wie eine echte Einsendung.
Stolperfallen
| Problem | Was zu tun ist |
|---|---|
Immer noch 405 nach der Änderung am Formular | Das Deployment, das du testest, wurde vor der Änderung gebaut, oder ein JavaScript-Handler sendet mit fetch an deinen eigenen Pfad. Durchsuche die gebauten Dateien nach /api/contact. |
400 Bad Request bei jedem Post oder 404 not_found von Ikiform | Die Formular-ID hat den Build nicht erreicht, oder es ist die falsche ID für diese Umgebung. Prüfe, ob die Variable für diese Umgebung existiert, und deploye neu. |
| Previews funktionieren, Posts aus der Produktion landen im Spam | Deine Produktionsdomain fehlt unter Allowed domains des Produktionsformulars. Füge die eigene Domain sowie your-project.vercel.app hinzu. |
| Der Browser landet auf der Dankeseite von Ikiform statt auf deiner | _redirect wird ignoriert, außer seine Domain steht unter Allowed domains und es ist eine https:-URL. Füge die Domain hinzu oder setze stattdessen Redirect after submit in den Einstellungen des Formulars. |
Fragen
Das Formular sendet an einen Pfad, hinter dem nur eine statische Datei liegt. Vercel liefert Dateien für GET-Requests aus, und ohne Funktion für diesen Pfad lehnt es den POST mit 405 ab. Sende das Formular an ein Formular-Backend oder füge eine Vercel Function hinzu.
Nein. Server Actions werden mit output: export nicht unterstützt, weil es keinen Server gibt, der sie ausführt. Sende aus einer Client-Komponente an ein Formular-Backend oder deploye ohne statischen Export, damit Vercel die Action ausführt.
Besser nicht. Gib Previews über eine Preview-Umgebungsvariable eine eigene Formular-ID, damit Testnachrichten weder dein Postfach noch deine Webhooks erreichen.
Nein. Das Formular funktioniert auch ohne. Füge vercel.json nur hinzu, um eine Content-Security-Policy zu setzen, die ikiform.com erlaubt, oder um cleanUrls für eine aufgeräumtere Danke-URL zu aktivieren.
Nein, es gibt keinen kostenlosen Tarif. Testen ist kostenlos: Du kannst 50 Testeinsendungen pro Monat von localhost senden, bevor du zahlst. Für den Livebetrieb startet Pro bei $6/Monat für 500 Einsendungen, oder du zahlst einmalig 10.000 Einsendungen für $49 oder 50.000 Einsendungen für $149, mit Guthaben, das nie verfällt.
Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.