```html

Deploying a Static Charter Proposal Page: S3 + CloudFront Cache Invalidation Workflow

What Was Done

Created and deployed a new static HTML proposal page for a yacht charter service at queenofsandiego.com/proposals/jada-charter-proposal-sue.html. The file was authored as a standalone HTML document, pushed to an S3 bucket, and cache invalidated through CloudFront to ensure immediate global availability.

Technical Details

File Creation & Structure

The proposal page was created at:

/Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html

The document is a self-contained HTML file with embedded CSS and JavaScript, avoiding external stylesheet dependencies to reduce latency and ensure consistent rendering. Key sections include:

  • Header/Title Section: Greeting and proposal overview
  • Pricing Options: Two distinct service tiers (Option A: 2-hr, Captain only; Option B: 3-hr, full crew)
  • Legal Compliance Notice: USCG licensing and EPA/MPRSA ash scattering regulations (important for maritime proposals)
  • Historical Content: Hollywood history context (Bogart, Bacall, Flynn, Wayne era references)
  • Call-to-Action: Contact form linking to bookings@queenofsandiego.com

S3 Deployment

The file was uploaded to the S3 bucket serving the website. The deployment command followed this pattern:

aws s3 cp proposals/jada-charter-proposal-sue.html s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html --acl public-read

The --acl public-read flag ensures the object is publicly readable, which is necessary for CloudFront to serve it to end users. The file is stored with standard HTTP headers (no custom cache-control directives at upload time; caching is managed at the CloudFront distribution level).

Infrastructure: CloudFront Cache Invalidation

After S3 upload, cache invalidation was triggered to ensure the new page appeared immediately on the CDN edge locations. This is critical for proposal URLs that may be shared with prospective customers.

Invalidation Command:

aws cloudfront create-invalidation --distribution-id <DISTRIBUTION_ID> --paths "/proposals/jada-charter-proposal-sue.html"

The distribution ID is stored in environment variables (sourced from /Users/cb/Documents/repos/.secrets/repos.env) and referenced in the deployment workflow. The create-invalidation API call purges the file from all CloudFront edge caches globally, forcing a fresh fetch from the S3 origin on the next request.

Invalidation Status Check:

aws cloudfront get-invalidation --distribution-id <DISTRIBUTION_ID> --id <INVALIDATION_ID>

This command returns the status (Completed/InProgress) of the cache clear operation. Invalidations typically complete within 60 seconds but can take up to 15 minutes in rare cases.

Why This Architecture

  • Static HTML over Dynamic: A proposal page has no server-side logic—it's read-only content. Serving static HTML minimizes latency, eliminates server overhead, and simplifies security. No database queries, no backend processing.
  • S3 + CloudFront Combination: S3 provides durable object storage at scale; CloudFront is the global CDN layer. This pattern delivers content from edge locations near the end user, reducing latency. For a yacht charter proposal, fast load times on mobile (common use case for sharing booking links) matter.
  • Cache Invalidation Strategy: Rather than setting short TTLs (Time-To-Live) on every asset, explicit invalidation is used. This allows the page to be cached aggressively (long TTL) for performance while enabling immediate updates when needed. The cost trade-off is minimal (CloudFront allows 3,000 invalidation paths per month free; additional paths cost $0.005 each).
  • Proposal as Standalone File: The file contains embedded CSS and minimal images (or image references to pre-existing assets). This reduces the number of HTTP requests and keeps the proposal portable—it can be shared as a single URL without worrying about external dependencies breaking.

Key Implementation Decisions

1. Self-Contained HTML

CSS is embedded in <style> tags rather than linked externally. This eliminates render-blocking resource chains and ensures the page renders consistently even if external stylesheets become unavailable. For a proposal (which may be shared in email, Slack, etc.), this is safer.

2. Two Pricing Tiers with Clear Recommendation

The page presents exactly two options to reduce decision fatigue. Option A is marked as recommended, providing a default path for customers uncertain about their needs. This follows conversion optimization best practices—too many choices lead to abandonment.

3. Legal Compliance Content

Maritime law (USCG licensing, EPA/MPRSA regulations for ash scattering) is documented on the proposal itself. This shifts liability; the customer has clear, written notice of legal constraints and the operator's licensing status. This is not just good UX but necessary legal documentation for a charter service.

4. Environment Variable Management

The deployment script sources credentials and IDs from .secrets/repos.env, which is excluded from version control. This prevents accidental exposure of AWS account IDs, distribution IDs, or other infrastructure details in git history.

Deployment Workflow Summary

# 1. Source environment variables
set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a

# 2. Upload to S3
aws s3 cp proposals/jada-charter-proposal-sue.html s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html --acl public-read

# 3. Invalidate CloudFront cache
aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DIST_ID --paths "/proposals/jada-charter-proposal-sue.html"

This workflow is idempotent—running it multiple times produces the same result. The S3 upload overwrites the previous version, and the CloudFront invalidation clears stale cache regardless of prior state.

What's Next

  • Image Assets: Integrate hero/interior photos (file paths to be confirmed; currently referenced via existing asset directories at /Users/cb/Documents/repos/sites/queenofsandiego.com/assets/images/).
  • Contact Integration: Wire up the email form to a backend service or third-party form handler (e.g., Formspree, AWS SES via Lambda).
  • A/B Testing: Once the page is live, track click-through and conversion rates to the booking email. Use CloudFront access logs (S3-stored) to measure traffic and user behavior.
  • Monitoring: Set up CloudFront alarms for 4xx/5xx error spikes, and