Einrichtung von CloudFront zum Hosten Ihrer Web App

Avatar of Adam Rackis
Adam Rackis am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

In meinem letzten Artikel haben wir besprochen, wie man eine Web-App einrichtet, die CSS- und JavaScript-Chunks und -Bundles von CloudFront ausliefert. Wir haben sie in Vite integriert, sodass die vom Stamm-HTML-File der App angeforderten Assets von CloudFront als CDN bezogen werden, wenn die App im Browser ausgeführt wird.

Obwohl das Edge-Caching von CloudFront Vorteile bietet, ist die Auslieferung der Ressourcen Ihrer App von diesen verschiedenen Standorten nicht ohne eigene Kosten. Schauen wir uns eine WebPageTest-Spur meiner eigenen Web-App an, die mit der Konfiguration aus dem letzten Blogbeitrag ausgeführt wird.

Beachten Sie die langen Verbindungszeiten für die Zeilen 2-4. Zeile 1 ist unser HTML-Einstiegspunkt. Dieses HTML wird analysiert, der Browser erkennt Skript- und Link-Tags für die JavaScript- und CSS-Assets, die sich auf dem CDN befinden, und fordert diese an. Dies führt zum Aufbau einer neuen Verbindung, was, wie Sie sehen können, Zeit beansprucht.

Dieser Beitrag zeigt Ihnen, wie Sie das umgehen können. Wir werden Schritt für Schritt durchgehen, wie Sie die *gesamte* Web-App auf CloudFront hosten und CloudFront nicht-cachebare Anfragen für Daten, Authentifizierung usw. an unseren zugrunde liegenden Webserver weiterleiten oder „proxen“.

Beachten Sie, dass dies wesentlich mehr Arbeit bedeutet als in dem letzten Artikel, und die Anweisungen wahrscheinlich für Sie unterschiedlich sein werden, abhängig von den genauen Anforderungen Ihrer Web-App, sodass Ihre Ergebnisse variieren können. Wir werden DNS-Einträge ändern und je nach Ihrer Web-App müssen Sie möglicherweise einige Cache-Header hinzufügen, um zu verhindern, dass bestimmte Assets überhaupt gecacht werden. Wir werden uns mit all dem befassen!

Sie fragen sich vielleicht, ob die im letzten Artikel behandelte Einrichtung angesichts dessen, was wir in diesem Artikel tun, überhaupt Vorteile bietet. Angesichts der langen Verbindungszeit wären wir dann besser dran gewesen, das CDN zu umgehen und stattdessen alle unsere Assets vom Webserver auszuliefern, um diese längere Wartezeit zu vermeiden? Ich habe dies mit meiner eigenen Web-App gemessen, und die CDN-Version war tatsächlich schneller, aber nicht viel. Der anfängliche LCP-Seitenaufruf war etwa 200-300 ms schneller. Und denken Sie daran, das ist nur für den ersten Aufruf. Sobald diese Verbindung hergestellt ist, sollte das Edge-Caching für alle nachfolgenden, asynchron geladenen Chunks deutlich mehr Wert bieten.

Einrichtung unseres DNS

Unser Endziel ist es, unsere gesamte Web-App von CloudFront auszuliefern. Das bedeutet, wenn wir unsere Domain aufrufen, möchten wir, dass die Ergebnisse von CloudFront kommen und nicht von dem Webserver, mit dem sie derzeit verbunden ist. Das bedeutet, wir müssen unsere DNS-Einstellungen ändern. Wir werden dafür AWS Route 53 verwenden.

Ich verwende mydemo.technology als Beispiel, eine Domain, die mir gehört. Ich werde Ihnen hier alle Schritte zeigen. Bis Sie dies jedoch lesen, werde ich diese Domain aus meiner Web-App entfernt haben. Wenn ich Ihnen also später tatsächliche CNAME-Einträge und ähnliches zeige, werden diese nicht mehr vorhanden sein.

Gehen Sie zur Route 53-Homepage und klicken Sie auf „Hosted Zones“.

Showing the hosted zone configuration screen in the CloudFront settings.

Klicken Sie auf „Hosted Zone erstellen“ und geben Sie die Domain der App ein.

Notieren Sie sich nun die Namen der Nameserver, die auf dem nächsten Bildschirm aufgeführt sind. Sie sollten etwa so aussehen.

Wir haben noch nichts wirklich erreicht. Wir haben AWS mitgeteilt, dass wir möchten, dass es diese Domain für uns verwaltet, und AWS hat uns die Nameserver gegeben, über die es unseren Traffic leiten wird. Um dies wirksam werden zu lassen, müssen wir dorthin gehen, wo unsere Domain registriert ist. Dort sollte es eine Möglichkeit geben, Ihre eigenen benutzerdefinierten Nameserver einzugeben.

Beachten Sie, dass meine Domain bei GoDaddy registriert ist und dies in den Screenshots in diesem Artikel widergespiegelt wird. Die Benutzeroberfläche, Einstellungen und Optionen können von dem abweichen, was Sie bei Ihrem Registrar sehen.

Warnung: Ich empfehle, die ursprünglichen Nameserver sowie alle DNS-Einträge zu notieren, bevor Sie Änderungen vornehmen. So haben Sie im Falle eines Fehlschlags alles, was Sie benötigen, um zum Zustand vor Beginn der Änderungen zurückzukehren. Und selbst wenn alles gut funktioniert, möchten Sie trotzdem alle anderen Einträge in Route 53, z.B. MX-Einträge usw., wieder hinzufügen.

Einrichtung einer CloudFront-Distribution

Lassen Sie uns eine CloudFront-Distribution erstellen, um unsere Web-App zu hosten. Die Grundlagen haben wir im letzten Beitrag behandelt, also machen wir direkt weiter. Eine große Änderung gegenüber dem letzten Mal ist, was wir für die Ursprungsdomäne eingeben. Geben Sie nicht die Top-Level-Domain ein, z. B. your-app.net. Was Sie benötigen, ist die zugrunde liegende Domain, auf der Ihre App gehostet wird. Wenn dies Heroku ist, geben Sie die von Heroku bereitgestellte URL ein.

Ändern Sie als Nächstes unbedingt das Standardprotokoll, wenn Sie diese Website über eine sichere HTTPS-Verbindung nutzen möchten.

Dieser Teil ist entscheidend. Wenn Ihre Web-App Authentifizierung, Hosting von Daten oder andere Dinge ausführt, stellen Sie sicher, dass Sie neben GET auch andere Verben aktivieren. Wenn Sie diesen Teil überspringen, werden alle POST-Anfragen für Authentifizierung, Datenänderung usw. abgelehnt und schlagen fehl. Wenn Ihre Web-App nichts anderes tut, als Assets bereitzustellen, und all diese Dinge von externen Diensten übernommen werden, dann ist das hervorragend! Sie haben eine großartige Konfiguration und können diesen Schritt überspringen.

Wir müssen im Vergleich zum letzten Mal einige Änderungen an den Einstellungen für den Cache-Schlüssel und die Ursprungsanfragen vornehmen.

Wir müssen eine Cache-Richtlinie mit einer minimalen TTL von 0 erstellen, damit nicht cachebare Header, die wir zurücksenden, ordnungsgemäß beachtet werden. Möglicherweise möchten Sie auch alle Query-Strings aktivieren. Ich hatte seltsames Verhalten beobachtet, als mehrere GraphQL-Anfragen zusammen mit unterschiedlichen Query-Strings herausgingen, die ignoriert wurden, wodurch alle diese Anfragen aus Sicht von CloudFront identisch erschienen.

Meine Richtlinie sah am Ende so aus:

Für eine Ursprungsanforderungsrichtlinie sollten wir, falls erforderlich, sicherstellen, dass Query-Strings und Cookies gesendet werden, damit Dinge wie Authentifizierung und Datenabfragen funktionieren. Um es klar auszudrücken: Dies bestimmt, ob Cookies und Query-Strings von CloudFront an Ihren Webserver (z. B. Heroku oder ähnliches) gesendet werden.

Meine sieht so aus:

Zuletzt können wir für die Antwortheader-Richtlinie „CORS mit Preflight“ aus der Liste auswählen. Am Ende werden Ihre ersten beiden unterschiedliche Namen haben, je nachdem, wie Sie sie eingerichtet haben. Aber meine sieht so aus:

Lassen Sie uns unsere Domain, was auch immer sie ist, mit dieser CloudFront-Distribution verbinden. Leider ist das mehr Arbeit, als Sie vielleicht erwarten. Wir müssen AWS beweisen, dass wir die Domain tatsächlich besitzen, denn so gut Amazon auch weiß, das tun wir nicht. Wir haben eine Hosted Zone in Route 53 erstellt. Und wir haben die Nameserver, die sie uns gegeben hat, bei GoDaddy (oder wem auch immer Ihre Domain gehört) registriert. Aber Amazon weiß das noch nicht. Wir müssen Amazon beweisen, dass wir tatsächlich die DNS-Kontrolle über diese Domain haben.

Zuerst werden wir ein SSL-Zertifikat anfordern.

Als Nächstes fordern wir den Zertifikatslink an.

Nun wählen wir die Option zur Anforderung eines öffentlichen Zertifikats aus.

Wir müssen die Domain angeben.

Und in meinem Fall ist das Zertifikat ausstehend.

Also klicke ich darauf.

Dies beweist, dass wir diese Domain besitzen und kontrollieren. Öffnen Sie in einem separaten Tab wieder Route 53 und öffnen Sie unsere Hosted Zone.

Nun müssen wir den CNAME-Eintrag erstellen. Kopieren Sie den ersten Teil für den Record name. Wenn der CNAME beispielsweise _xhyqtrajdkrr.mydemo.technology lautet, geben Sie den Teil _xhyqtrajdkrr ein. Kopieren Sie für den Record value den gesamten Wert.

Unter der Annahme, dass Sie die AWS-Nameserver bei Ihrem Domain-Host, GoDaddy oder wem auch immer registriert haben, wird AWS bald in der Lage sein, den von Ihnen gerade erstellten DNS-Eintrag zu pingen, die erwartete Antwort zu sehen und Ihr Zertifikat zu validieren.

Es kann dauern, bis die von Ihnen anfangs gesetzten Nameserver propagiert sind. Theoretisch kann es bis zu 72 Stunden dauern, aber bei mir aktualisiert es sich normalerweise innerhalb einer Stunde.

Sie werden eine erfolgreiche Bestätigung für die Domain sehen.

...sowie für das Zertifikat.

Puh! Fast geschafft. Nun verbinden wir all dies mit unserer CloudFront-Distribution. Wir können zurück zum CloudFront-Einstellungen-Bildschirm gehen. Unter benutzerdefiniertes SSL-Zertifikat sollten wir das, was wir erstellt haben (und alle anderen, die Sie in der Vergangenheit erstellt haben), sehen.

Fügen Sie dann die Top-Level-Domain der App hinzu.

Alles, was noch zu tun ist, ist, Route 53 anzuweisen, unsere Domain an diese CloudFront-Distribution weiterzuleiten. Gehen wir also zurück zu Route 53 und erstellen einen weiteren DNS-Eintrag.

Wir müssen einen A-Eintrag für IPv4 und einen AAAA-Eintrag für IPv6 eingeben. Lassen Sie für beide den Record-Namen leer, da wir nur unsere Top-Level-Domain und nichts anderes registrieren.

Wählen Sie den A-Record-Typ. Geben Sie dann den Eintrag als Alias an und mappen Sie den Alias auf die CloudFront-Distribution. Dies sollte eine Option zum Auswählen Ihrer CloudFront-Distribution öffnen. Da wir die Domain zuvor bei CloudFront registriert haben, sollten Sie bei der Auswahl diese Distribution und nur diese Distribution sehen.

Wir wiederholen die genauen gleichen Schritte für den AAAA-Record-Typ, den wir für die IPv6-Unterstützung benötigen.

Führen Sie Ihre Web-App aus und stellen Sie sicher, dass sie tatsächlich, wissen Sie, funktioniert. Das sollte sie!

Zu testende und zu verifizierende Dinge

OK, obwohl wir technisch fertig sind, gibt es wahrscheinlich noch ein paar Dinge zu tun, um die genauen Anforderungen Ihrer Web-App zu erfüllen. Unterschiedliche Apps haben unterschiedliche Bedürfnisse und was ich bisher gezeigt habe, hat uns durch die gängigen Schritte geführt, um Dinge für eine bessere Leistung durch CloudFront zu leiten. Wahrscheinlich gibt es Dinge, die für Ihre App einzigartig sind und mehr Aufmerksamkeit erfordern. Daher werde ich für diese einige mögliche zusätzliche Punkte behandeln, denen Sie bei der Einrichtung begegnen könnten.

Stellen Sie zunächst sicher, dass alle POST-Anfragen korrekt an Ihren Ursprung gesendet werden. Unter der Annahme, dass CloudFront korrekt konfiguriert ist, um Cookies an Ihren Ursprung weiterzuleiten, sollte dies bereits funktionieren, aber es schadet nicht, dies zu überprüfen.

Das größere Problem sind alle anderen GET-Anfragen, die an Ihre Web-App gesendet werden. Standardmäßig werden alle GET-Anfragen, die CloudFront empfängt, wenn sie gecacht sind, mit der gecachten Antwort an Ihre Web-App ausgeliefert. Dies kann katastrophal sein. Alle Datenanfragen an REST- oder GraphQL-Endpunkte, die mit GET gesendet werden, werden vom CDN gecacht. Und wenn Sie einen Service Worker versenden, wird auch dieser gecacht, anstatt des normalen Verhaltens, bei dem die aktuelle Version im Hintergrund heruntergeladen und bei Änderungen aktualisiert wird.

Um CloudFront mitzuteilen, dass bestimmte Dinge *nicht* gecacht werden sollen, stellen Sie sicher, dass der Header "Cache-Control" auf "no-cache" gesetzt ist. Wenn Sie ein Framework wie Express verwenden, können Sie mit Middleware für Ihren Datenzugriff etwas wie folgt einrichten:

app.use("/graphql", (req, res, next) => {
  res.set("Cache-Control", "no-cache");
  next();
});
app.use(
  "/graphql",
  expressGraphql({
    schema: executableSchema,
    graphiql: true,
    rootValue: root
  })
); 

Für Dinge wie Service Worker können Sie spezifische Regeln für diese Dateien vor Ihrer statischen Middleware einfügen.

app.get("/service-worker.js", express.static(__dirname + "/react/dist", { setHeaders: resp => resp.set("Cache-Control", "no-cache") }));
app.get("/sw-index-bundle.js", express.static(__dirname + "/react/dist", { setHeaders: resp => resp.set("Cache-Control", "no-cache") }));
app.use(express.static(__dirname + "/react/dist", { maxAge: 432000 * 1000 * 10 }));

Und so weiter. Testen Sie alles gründlich, da so viel schiefgehen kann. Und nach jeder Änderung, die Sie vornehmen, führen Sie unbedingt eine vollständige Invalidierung in CloudFront durch und löschen Sie den Cache, bevor Sie Ihre Web-App erneut ausführen, um zu testen, ob Dinge korrekt vom Cache ausgeschlossen sind. Sie können dies über den Tab Invalidations in CloudFront tun. Öffnen Sie diesen und geben Sie /* als Wert ein, um alles zu löschen.

Eine funktionierende CloudFront-Implementierung

Nachdem wir nun alles zum Laufen gebracht haben, lassen Sie uns unsere Spuren in WebPageTest erneut ausführen.

Und genau so haben wir keine Verbindungsaufbauten mehr wie zuvor für unsere Assets. Bei meiner eigenen Web-App sah ich eine deutliche Verbesserung von 500 ms beim LCP. Das ist ein solider Gewinn!


Das Hosten einer gesamten Web-App auf einem CDN kann das Beste aus allen Welten bieten. Wir erhalten Edge-Caching für statische Ressourcen, aber ohne die Verbindungskosten. Leider ist diese Verbesserung nicht kostenlos. Das korrekte Einrichten des gesamten notwendigen Proxyings ist nicht ganz intuitiv, und dann gibt es noch die Notwendigkeit, Cache-Header einzurichten, um zu verhindern, dass nicht cachebare Anfragen im Cache des CDNs landen.