# Ein Kontaktformular in eine statische Website auf Amazon S3 einbauen

> S3-Website-Endpunkte akzeptieren nur GET und HEAD über einfaches HTTP, also sende das Kontaktformular an ein Formular-Backend und setze Weiterleitungen mit CloudFront richtig.

Veröffentlicht am 2026-10-25 · https://ikiform.com/de/guides/contact-form-static-site/aws-s3

Ein S3-Website-Endpunkt beantwortet einen Formular-`POST` mit `405 MethodNotAllowed`, also richte die `action` des Formulars stattdessen auf deinen Ikiform-Endpunkt aus. Weil der Endpunkt einfaches `http:` ist, wird einem `_redirect` zurück dorthin nie gefolgt; setze **Redirect after submit** in den Einstellungen des Formulars oder schalte CloudFront für `https:` davor. Mit CloudFront vor dem REST-Endpunkt des Buckets leitest du auf einen vollständigen Dateinamen wie `thanks.html` weiter, weil das Default Root Object nur für die Wurzel gilt.

## Was bei S3 anders ist

Ein für statisches Website-Hosting konfigurierter Bucket bekommt einen eigenen Website-Endpunkt, etwa `http://www.example.com.s3-website-us-east-1.amazonaws.com`. AWS dokumentiert, dass er für Objekte nur `GET`- und `HEAD`-Requests unterstützt. Ein Formular-Post bekommt eine HTML-Fehlerseite:

`Antwort auf einen 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>
```

Drei weitere Eigenschaften von S3 prägen das Kontaktformular:

| Eigenschaft von S3 | Was das für das Formular bedeutet |
| --- | --- |
| Website-Endpunkte unterstützen kein HTTPS | Ikiform folgt einem `_redirect` nur zu einer `https:`-URL, daher wird eine Weiterleitung zurück auf den reinen Endpunkt ignoriert |
| Response-Header kommen von CloudFront, nicht vom Bucket | Eine Content-Security-Policy wird in einer CloudFront-Response-Headers-Policy gesetzt |
| Das Default Root Object von CloudFront gilt nur für die Wurzel | Mit einem REST-Origin lädt `https://example.com/thanks/` nicht `thanks/index.html`; leite auf `thanks.html` weiter |

Für eine `https:`-Site empfiehlt AWS Amplify Hosting, das auf CloudFront läuft, oder CloudFront vor dem Bucket. Das Formular funktioniert so oder so. Was sich ändert, ist, wo der Besucher nach dem Senden landet.

Diagramm: Wo der Besucher nach dem Senden landet, mit und ohne CloudFront

1. Auf dem reinen Website-Endpunkt wird die Seite über http ausgeliefert und sendet an Ikiform.
2. Ikiform speichert die Nachricht, ignoriert aber einen _redirect auf eine http-Adresse und zeigt seine gehostete Dankeseite.
3. Redirect after submit im Dashboard zu setzen, umgeht das, weil es http-URLs akzeptiert.
4. Hinter CloudFront mit deiner eigenen Domain ist die Seite https.
5. Ikiform folgt dem _redirect zu deiner thanks.html, solange die Domain unter Allowed domains steht.

Die Nachricht wird so oder so gespeichert. Nur der letzte Schritt, wohin der Browser geht, hängt von https ab.

## Welcher CloudFront-Origin

CloudFront kann auf zwei Arten aus dem Bucket lesen, und die Wahl entscheidet, wie deine Danke-URL aussehen muss:

| Origin | Bucket | Index-Seiten von Ordnern | Danke-URL |
| --- | --- | --- | --- |
| Der S3-Website-Endpunkt als Custom Origin | Muss öffentlich lesbar sein | Funktionieren: `/thanks/` lädt `thanks/index.html` | `/thanks/` oder `/thanks.html` |
| Der REST-Endpunkt mit Origin Access Control | Bleibt privat, nur von CloudFront lesbar | Nur die Wurzel, über das Default Root Object | `/thanks.html`, ein vollständiger Dateiname |

Ein vollständiger Dateiname funktioniert mit beiden, deshalb verwenden die Beispiele einen. Wenn du CloudFront nicht selbst betreiben willst, kann Amplify Hosting aus dem Bucket mit `https:` und einem verwalteten CDN deployen; auch dort braucht das Formular keine Änderung.

## Das Formular

S3-Sites sind oft Firmen-Sites, daher fragt dieses Formular nach einer geschäftlichen E-Mail-Adresse und dem Firmennamen. Lade es als `contact.html` neben `thanks.html` hoch:

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

- Der `_redirect` zeigt auf die CloudFront-Domain, mit Dateinamen. Ihm wird nur gefolgt, wenn `www.example.com` oder `example.com` unter **Allowed domains** steht.
- Auf dem reinen `http:`-Endpunkt entfernst du das `_redirect`-Eingabefeld und setzt stattdessen **Redirect after submit** auf die Dankeseite. Diese Einstellung akzeptiert `http:`-URLs, weil nur dein Team sie ändern kann.
- Behalte `email` als Feldnamen. Benachrichtigungs-E-Mails nutzen es als Reply-To-Adresse.

Auf einer Firmen-Site liest meist mehr als eine Person das Kontaktpostfach. Füge dem Formular bis zu 10 bestätigte Benachrichtigungsempfänger hinzu; jeder bekommt die Nachricht, und wer auf **Reply** klickt, antwortet direkt dem Absender. Wenn Nachrichten auch einen gemeinsamen Kanal oder eine Ticket-Queue erreichen sollen, füge einen signierten Webhook hinzu und leite ihn über [Zapier](https://ikiform.com/blog/form-submissions-to-zapier.md) oder deinen eigenen Code weiter. Das Feld `company` kommt als reiner Text an, du kannst also an beiden Stellen direkt danach filtern.

Teste die Seite vor dem Upload über einen lokalen Server mit `localhost` unter **Allowed domains**. Posts von `localhost` zählen als Tests, du kannst das Formular also fertigstellen, bevor der Bucket es überhaupt ausliefert.

Synchronisiere den Ordner mit dem Bucket und invalidiere die Seiten in CloudFront, damit das neue Formular sofort ausgeliefert wird:

`Terminal`:

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

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

## Erlaubte Domains auf AWS-Hosts

| Wo die Seite ausgeliefert wird | Hinzufügen |
| --- | --- |
| Reiner Website-Endpunkt | Der vollständige Endpunkt-Host des Buckets, etwa `www.example.com.s3-website-us-east-1.amazonaws.com` |
| Standarddomain von CloudFront | Der genaue Host `d111111abcdef8.cloudfront.net`, während du testest |
| Deine eigene Domain | `example.com`, was auch `www.example.com` abdeckt |

Füge niemals `amazonaws.com`, ein regionales `s3-website`-Suffix oder `cloudfront.net` allein hinzu. Jeder Bucket oder jede Distribution der Welt würde passen. AWS trägt die Domains der S3-Website-Endpunkte aus demselben Grund in die Public Suffix List ein: Jeder Bucket ist eine eigene Site.

## Header über CloudFront

S3-Objektmetadaten können keine Content-Security-Policy setzen, also setze sie in einer CloudFront-Response-Headers-Policy und hänge sie an das Default Cache Behavior der Distribution. Der Header-Wert ist auf 1.783 Zeichen begrenzt:

`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` ersetzt jede CSP, die der Origin sendet. Bei einem reinen S3-Origin gibt es keine, aber so behält die Policy die Kontrolle, falls du später den Origin wechselst.

## Prüfen, ob es funktioniert

1. Füge die Domains aus der Tabelle oben hinzu, plus `localhost`, während du testest.
2. Sende von deiner CloudFront-Domain ab. Du solltest auf `thanks.html` auf deiner eigenen Domain landen.
3. Wenn du den reinen Website-Endpunkt ebenfalls öffentlich lässt, sende auch von dort ab und prüfe, ob du auf der Seite landest, die unter **Redirect after submit** eingestellt ist.
4. Öffne **Submissions** im Dashboard. Die neue Einsendung ist da, mit jedem Feld, das du gesendet hast.
5. 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.
6. Nichts angekommen? Stell den Statusfilter im Dashboard auf **Spam**. Spam bekommt dieselbe Erfolgsantwort wie eine echte Einsendung.

## Stolperfallen

| Problem | Was zu tun ist |
| --- | --- |
| `405 Method Not Allowed` mit `Code: MethodNotAllowed` | Das Formular hat an den Bucket gesendet. CloudFront liefert vielleicht noch die alte `contact.html` aus; invalidiere sie. |
| `AccessDenied` oder `403` nach der Weiterleitung | Die Weiterleitung zeigt auf einen Ordner wie `/thanks/` in einer CloudFront-Distribution mit REST-Origin. Lass sie auf `thanks.html` zeigen. |
| 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. |
| `403 origin_not_allowed`, oder jeder Post landet im Spam | Die Domain der Seite steht nicht unter **Allowed domains** des Formulars. Füge sie hinzu, plus `localhost` für lokale Tests. Subdomains einer eingetragenen Domain sind erlaubt. |

## Fragen

### Kann eine statische S3-Website Formulareinsendungen verarbeiten?

Nein. S3-Website-Endpunkte unterstützen nur GET- und HEAD-Requests, daher bekommt ein POST 405 MethodNotAllowed. Richte das Formular auf ein Formular-Backend aus oder bau eine API mit Lambda.

### Brauche ich CloudFront, damit das Kontaktformular funktioniert?

Nein. Das Formular sendet auch von einer http-Seite an Ikiform. CloudFront gibt dir https, wodurch Ikiform einem _redirect zurück auf deine eigene Dankeseite folgen kann.

### Warum nicht API Gateway, Lambda und SES?

Das funktioniert und hält alles in AWS, aber Validierung, Spamfilter, Speicherung und Wiederholungsversuche baust du selbst, und SES braucht einen verifizierten Absender. Für ein Kontaktformular ist ein Formular-Backend weniger Pflegeaufwand.

### Cacht CloudFront das alte Formular, nachdem ich es geändert habe?

Das kann passieren, bis die gecachte Kopie abläuft. Invalidiere contact.html nach jeder Änderung oder lade HTML mit einem kurzen Cache-Control max-age hoch, damit neue Formulare schnell live gehen.

### Ist Ikiform kostenlos?

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.

## Weitere Anleitungen

- [Kontaktformular auf einer statischen Site](https://ikiform.com/de/guides/contact-form-static-site.md): Bau ein funktionierendes Kontaktformular in eine statische Site ohne Server ein: wohin der POST geht, Spam, Antworten per E-Mail und Code für jeden Host und jedes Framework.
- [Cloudflare Pages](https://ikiform.com/de/guides/contact-form-static-site/cloudflare-pages.md): Bau ein Kontaktformular in Cloudflare Pages ein, ohne eine Function zu schreiben: an ein Formular-Backend senden, Header in _headers setzen und eigene Turnstile-Schlüssel nutzen.
- [GitHub Pages](https://ikiform.com/de/guides/contact-form-static-site/github-pages.md): GitHub Pages kann keinen Servercode ausführen, also bekommt ein Formular-POST 405 Not Allowed; sende an ein Formular-Backend und setze Weiterleitungen für Projekt-Sites richtig.
- [Vercel](https://ikiform.com/de/guides/contact-form-static-site/vercel.md): 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.
- [Netlify](https://ikiform.com/de/guides/contact-form-static-site/netlify.md): Betreib ein Kontaktformular auf einer Netlify-Site ohne Netlify Forms: keine Erkennung beim Build, JSON-Posts aus React und ein eigenes Formular für Deploy Previews.
- [Portfolio-Website](https://ikiform.com/de/guides/contact-form-static-site/portfolio-website.md): Ein Portfolio-Kontaktformular mit den Fragen, die du im ersten Gespräch stellen würdest: Projekt, Budget, Zeitplan und ein Link, ohne Server und mit Antworten aus deinem Postfach.
