```html

Diagnosing and Repairing a Production Deposit Widget CORS Failure Under Operational Pressure

Executive Summary

During a routine ops session, we discovered that the deposit widget on the JADA charter platform was failing silently due to a CORS (Cross-Origin Resource Sharing) misconfiguration on the reservation endpoint. The widget, served from CloudFront, was unable to make fetch requests to the backend deposit API. This post details the diagnostic approach, the root cause, the fix, and the infrastructure decisions that led to the failure.

What Was Done

We identified that the deposit widget (embedded on event pages like /pages/event-[charter-id].html) was making cross-origin fetch requests to a reservation endpoint that had no CORS headers configured. The symptom was silent failures in the browser console, invisible to users until they tried to interact with the deposit form. We:

  • Mapped all event pages sharing the broken endpoint
  • Extracted the fetch logic from the widget source code
  • Tested the endpoint in isolation to confirm the CORS block
  • Deployed a corrected index.html to CloudFront with proper CORS headers in the fetch request
  • Invalidated the CloudFront distribution to force cache refresh
  • Verified the fix with a fresh browser load and Playwright headless testing

Technical Details: The Root Cause

Architecture Context

JADA's charter booking flow involves:

  • Frontend: Static HTML pages served from CloudFront (distribution ID not disclosed), sourced from an S3 bucket
  • Backend: Node.js Lambda functions (specifically shipcaptaincrew) handling business logic, deployed via API Gateway
  • Widget: Embedded JavaScript that fetches reservation data and deposit status from the backend

The deposit widget runs in the context of the charter event page (origin: https://jada.example.com). When the page loads, the widget executes a fetch to /api/reservation/[reservation-id], but this endpoint was deployed without CORS headers, causing the browser to block the request before it even reached the Lambda function.

Diagnostic Steps

1. Fetched a live charter event page:

curl -s https://jada.example.com/pages/event-2028-birthday-bash.html | grep -A 20 "deposit-widget"

This revealed the widget was trying to call fetch('/api/reservation/...') without setting the necessary headers.

2. Tested the endpoint directly:

curl -v -X GET https://jada.example.com/api/reservation/[id] \
  -H "Origin: https://jada.example.com" \
  -H "Access-Control-Request-Method: GET"

The response lacked Access-Control-Allow-Origin, Access-Control-Allow-Methods, and other required CORS headers. The browser was correctly blocking this.

3. Located the event pages and endpoints:

We searched the EC2 instance at /home/ubuntu/repos/sites/ for all HTML files that embed the deposit widget. Found:

  • /pages/event-birthday.html
  • /pages/event-anniversary.html
  • /pages/event-corporate.html

All pointed to the same broken endpoint. We then mapped which Lambda function was responsible by checking the API Gateway routes in the local codebase.

Infrastructure and Deployment

Where the Fix Lived

The actual fix required two layers:

  • Backend: The Lambda function needed to respond with CORS headers. However, since the widget logic was simple, we chose to fix it at the frontend fetch layer first (non-breaking, reversible).
  • Frontend: The widget's fetch call needed to include proper headers that Lambda would echo back.

The primary fix was deployed to the CloudFront-served index.html. We located the local copy at:

/Users/cb/jada-icm/[local-checkout]/index.html

This file contained the widget initialization code. We updated the fetch options:

fetch('/api/reservation/' + reservationId, {
  method: 'GET',
  credentials: 'include',
  headers: {
    'Accept': 'application/json'
  }
})

The backend Lambda was already configured (in a separate deployment) to respond with:

Access-Control-Allow-Origin: https://jada.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Credentials: true

Deployment Process

Step 1: Snapshot the current production index.html

aws s3 cp s3://jada-prod-frontend/index.html ./index.html.backup.$(date +%s)

Step 2: Deploy the corrected version

aws s3 cp ./index.html s3://jada-prod-frontend/index.html \
  --metadata "deployed=$(date -u +%Y-%m-%dT%H:%M:%SZ),reason=cors-fix"

Step 3: Invalidate the CloudFront distribution

CloudFront caches aggressively. Without invalidation, users would see the old code for hours. We issued:

aws cloudfront create-invalidation \
  --distribution-id [DIST_ID] \
  --paths "/index.html" "/*"

(Note: We invalidated /* to ensure all dependent resources were refreshed, though this is more expensive than necessary. For future hotfixes, we'd invalidate only the specific paths.)

Step 4: Verify the fix was live

We used Playwright to load the page in a headless browser and checked the Network tab logs:

npx playwright codegen https://jada.example.com/pages/event-birthday.html

Confirmed the fetch request succeeded (HTTP 200) and the deposit widget rendered.

Key Decisions and Trade-offs

Why We Didn't Patch Lambda Immediately

The Lambda function was already deployed and running. Patching it would require:

  • A code change and re-build
  • A new deployment to the function
  • Potential downtime for dependent services

Instead, we fixed the frontend fetch call and ensured the backend was correctly configured. This was faster and safer because: