JADA's lead captain Darrell needed a personal landing page tied to the Queen of San Diego
brand — somewhere guests and the operations side could see his availability, open charter slots,
and a claim flow without going through the main booking funnel. The decision was to give him a
first-party subdomain (darrell.queenofsandiego.com) rather than a path under the
main site, so his page could evolve independently without touching the main CloudFront
distribution's cache behavior rules.
The stack is the same pattern used for every JADA property: S3 static hosting behind a CloudFront distribution, with a Route 53 alias record pointing the subdomain at the CF distribution domain. The sequence was:
E3CZ5XE9HGYQT9) pointed at the S3 origindarrell.queenofsandiego.com → E3CZ5XE9HGYQT9The S3 policy issue is the part that took a few minutes. The bucket had a condition block
that matched only the original CF distribution's ARN. Adding a second distribution means adding
a second aws:SourceArn to the condition, or switching to a wildcard on the account
prefix. We went with the explicit ARN to stay auditable. Once that was corrected and the
distribution finished deploying, the page came up clean.
While reviewing staging, three images on the cortez charter page (main boat shot,
Gucci bag detail, cockpit view) were returning 404s. All three had been sourced from Unsplash
URLs that had rotated or expired — a recurring problem with third-party image hosting. The fix
was to replace each <img src> with a path to the S3-hosted asset, then issue a
targeted CloudFront invalidation for /cortez/* so the stale responses cleared
immediately. The invalidation ran in under 60 seconds.
Unsplash's CDN URLs don't 301 or signal expiry — they just return a 404 with no cache headers, which means CloudFront will cache the 404 at its configured TTL. If you don't invalidate after replacing the source, the page stays broken even after you've fixed the HTML. Always pair the S3 swap with an invalidation.
The afternoon session also closed out a staging backlog. Thirty-four files moved to prod,
including eleven pages under captains-log/, a 404 page, concert-nights,
sd-sailing-calendar, and fourteen competitor charter comparison pages. The comparison
pages were all missing the GA4 snippet — they had been built before the site-wide GA4 rollout.
Rather than fixing each file by hand, the promotion script injected the snippet
programmatically before writing to S3:
GA4_SNIPPET = """<script async
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX">
</script>
<script>window.dataLayer=window.dataLayer||[];
function gtag(){dataLayer.push(arguments);}
gtag('js',new Date());gtag('config','G-XXXXXXX');
</script>"""
html = html.replace("</head>", GA4_SNIPPET + "</head>", 1)
After the S3 sync, a single wildcard invalidation (/*) cleared the CF cache
across both the main distribution and the competitor pages distribution. All 34 files were
confirmed live before the session closed.
An earlier operation had accidentally overwritten the crew roster DynamoDB record, wiping
several members. The afternoon session restored ten crew members to the jada-crew-roster
table by hand — Darrell, Gene, Chris, Travis, Rick, Scott, Dan, Angelia, Angela Wong, and
Daniela. One crew member (Abdul, a Sergio-recommended captain) remained missing because his
email address wasn't on file. The episode cemented a standing rule: DDB writes to the roster
table require a read-before-write confirmation step so a partial update never silently replaces
the full record.