Kontaktformular auf einer statischen Site

Kontaktformular auf einer statischen Site6 Min. Lesezeit

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.

Preet SutharGründer, Ikiform

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>
<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 S3Was das für das Formular bedeutet
Website-Endpunkte unterstützen kein HTTPSIkiform 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 BucketEine Content-Security-Policy wird in einer CloudFront-Response-Headers-Policy gesetzt
Das Default Root Object von CloudFront gilt nur für die WurzelMit 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.

Wo der Besucher nach dem Senden landet, mit und ohne CloudFrontreiner S3-Website-Endpunkt, httpCloudFront und deine Domain, httpshttp://…s3-website…contact.htmlIkiformspeichert, mailt dirDankeseite vonIkiform_redirect auf httpignorierthttps://www.example.comcontact.htmlIkiformspeichert, mailt dirdeine thanks.html_redirect gefolgtPOST303POST303Wo der Besucher nach dem Senden landet, mit und ohne CloudFrontreiner S3-Website-Endpunkt, httpCloudFront und deine Domain, httpshttp://…s3-website…contact.htmlIkiformspeichert, mailt dirDankeseite von Ikiform_redirect auf http ignorierthttps://www.example.comcontact.htmlIkiformspeichert, mailt dirdeine thanks.html_redirect gefolgtPOST303POST303
  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:

OriginBucketIndex-Seiten von OrdnernDanke-URL
Der S3-Website-Endpunkt als Custom OriginMuss öffentlich lesbar seinFunktionieren: /thanks/ lädt thanks/index.html/thanks/ oder /thanks.html
Der REST-Endpunkt mit Origin Access ControlBleibt privat, nur von CloudFront lesbarNur 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
<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 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
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 wirdHinzufügen
Reiner Website-EndpunktDer vollständige Endpunkt-Host des Buckets, etwa www.example.com.s3-website-us-east-1.amazonaws.com
Standarddomain von CloudFrontDer genaue Host d111111abcdef8.cloudfront.net, während du testest
Deine eigene Domainexample.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
{
  "Name": "site-csp",
  "SecurityHeadersConfig": {
    "ContentSecurityPolicy": {
      "Override": true,
      "ContentSecurityPolicy": "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"
    }
  }
}
Terminal
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

ProblemWas zu tun ist
405 Method Not Allowed mit Code: MethodNotAllowedDas Formular hat an den Bucket gesendet. CloudFront liefert vielleicht noch die alte contact.html aus; invalidiere sie.
AccessDenied oder 403 nach der WeiterleitungDie 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 SpamDie 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

Hängst du bei einem Schritt fest oder willst eine Anleitung für deinen Fall? Schreib an hello@ikiform.com.