Emergency Deposit Widget Recovery: CORS Remediation and CloudFront Invalidation on Production Charter Pages
During this session, we diagnosed and resolved a critical CORS failure affecting the deposit widget across JADA's charter booking flow. The widget, responsible for initiating Stripe payments on event detail pages, was failing silently due to cross-origin request blocking. This post covers the diagnosis methodology, the fix applied to production, and the infrastructure pattern that prevented wider cascading failures.
The Problem: Silent CORS Failure in Production
The deposit widget—embedded on charter event pages served via CloudFront—was attempting to POST reservation data to a backend endpoint. Browser console logs revealed a CORS preflight rejection: the endpoint was not returning Access-Control-Allow-Origin headers, and the request chain was breaking before the actual payment initiation could occur.
Initial investigation revealed:
- Event pages were being served correctly from the CloudFront distribution
- The fetch request was targeting the correct endpoint (identified via network inspection)
- The endpoint itself was reachable and responding, but without CORS headers
- The failure was read-only—no deposits were actually being attempted, but no useful error message was surfacing to users
The scope was unclear: we needed to know whether this affected one page, one endpoint, or the entire booking flow across all charter types.
Diagnosis: Systematic Endpoint Mapping
Rather than assume, we mapped the problem:
- Located the deposit widget source: Found the fetch block in the live charter page HTML, extracted the target endpoint URL, and confirmed it was hardcoded (not dynamically constructed).
- Tested endpoint CORS headers directly: Used curl and browser fetch to confirm the endpoint was returning 200 OK but missing CORS headers.
- Enumerated affected pages: Identified all event pages sharing the same endpoint—this included birthday charter pages, corporate charters, and sunset experiences. Each page was hitting the same backend, so a single fix would remediate all.
- Verified the fix scope: Confirmed the endpoint was not being called from other services (no webhook consumers, no Lambda functions depending on the old header behavior).
This systematic approach prevented us from deploying a fix that would have broken something downstream we hadn't checked.
The Fix: Endpoint Header Configuration
The remediation was applied to the backend service running on the EC2 instance. The endpoint—responsible for accepting reservation data before payment—needed to return proper CORS headers.
File modified: The backend service configuration on the Layer-3 EC2 instance (located via AWS Systems Manager; instance ID confirmed via region scan across us-east-1).
Change applied:
Access-Control-Allow-Origin: https://[cloudfront-distribution-domain]
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
The decision to use https://[cloudfront-distribution-domain] rather than * was deliberate: we restricted the origin to the actual CloudFront distribution serving our pages. This prevents the endpoint from accepting requests from arbitrary origins while still allowing the widget to function.
The endpoint also needed to handle preflight requests (OPTIONS) explicitly, returning the CORS headers without attempting to process the request body.
Deployment: CloudFront Invalidation
After confirming the backend fix was live, we needed to ensure browsers fetched fresh index.html and related page assets—cached versions might still have stale header expectations or broken widget logic.
Step 1: Snapshot current production state
# Capture the current index.html before any changes
aws s3 cp s3://[jada-bucket]/index.html index.html.backup.2026-06-02
Step 2: Verify the CloudFront distribution ID
Located the distribution via AWS CLI by filtering on the origin domain. The distribution ID was confirmed to be non-null and actively serving traffic.
Step 3: Invalidate the cache
aws cloudfront create-invalidation \
--distribution-id [DIST-ID] \
--paths "/*"
The /* pattern ensures all objects—not just index.html—are purged from edge caches across all CloudFront locations. This forces browsers to fetch fresh versions and pick up any header changes or widget logic fixes.
Step 4: Verify invalidation status
aws cloudfront get-invalidation \
--distribution-id [DIST-ID] \
--id [INVALIDATION-ID]
Invalidation status transitioned from InProgress to Completed, confirming the cache had been flushed.
Verification: End-to-End Widget Test
After invalidation completed, we verified the fix by:
- Opening a fresh browser instance (headless via Playwright for reproducibility)
- Navigating to a live event page served through the CloudFront distribution
- Inspecting the network tab to confirm the deposit widget's fetch request now returned the correct CORS headers
- Confirming the preflight request (OPTIONS) succeeded and the actual POST was not blocked
- Capturing a screenshot of the working widget state for the incident log
All charter types (birthday, corporate, sunset) showed the widget functioning correctly.
Key Infrastructure Decisions
- Restrict CORS origin rather than using wildcard: Reduces attack surface and prevents accidental exposure to third-party origins.
- Snapshot before deploy: Provides rollback capability if the change introduced unexpected behavior.
- Blanket CloudFront invalidation (/*) rather than selective: Guarantees consistency across all pages and eliminates edge cases where a partial invalidation might miss dependent assets.
- Verify via headless browser, not manual testing: Eliminates cache-related false positives and produces reproducible evidence.
What's Next
The immediate crisis is resolved: the deposit widget is operational, CORS headers are in place, and CloudFront is serving fresh assets. However, this incident revealed a gap in our automated integration testing—a CORS failure of this magnitude should have been caught before production.
Recommended follow-up work includes setting up synthetic monitoring on the deposit endpoint (OPTIONS and POST requests) and integrating CORS header validation into the deployment pipeline.