```html

Creating and Deploying a Dynamic Charter Proposal Page: Multi-Environment Static Site Publishing with S3 and CloudFront

What Was Done

Built and deployed a new charter proposal page at queenofsandiego.com/proposals/jada-charter-proposal-sue.html that was previously missing from the production site, despite existing as a template in the repository. The page required multi-step infrastructure coordination: local HTML generation, S3 object creation, and CloudFront cache invalidation to ensure immediate availability across all edge locations.

Problem Statement

Card t-33502227 indicated an ash scattering charter proposal page should be accessible at a specific URL, but the generated HTML file did not exist in the published S3 bucket. The repository contained proposal templates and asset references, but the final compiled proposal HTML had never been pushed to production. This required understanding the full deployment pipeline from local development through CDN edge caching.

Technical Architecture and File Structure

The site uses a Git-based static site generation workflow:

  • Repository root: /Users/cb/Documents/repos/sites/queenofsandiego.com/
  • Proposal directory: /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/
  • Target S3 bucket: queenofsandiego.com (CloudFront origin)
  • Asset locations: /assets/images/interior/, /assets/images/
  • Deployment script: /Users/cb/Documents/repos/sites/queenofsandiego.com/publish_static_site.sh

All site credentials, AWS credentials, and distribution IDs are stored in /Users/cb/Documents/repos/.secrets/repos.env and sourced at deployment time using set -a / set +a pattern for non-interactive shell sourcing.

Implementation Steps

1. Repository Analysis and Content Discovery

Initial investigation confirmed the file structure:

find /Users/cb/Documents/repos/sites/queenofsandiego.com -name "*proposal*" 2>/dev/null | head -20
ls /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/

This revealed multiple proposal templates and reference files, but the specific compiled HTML for the charter proposal was absent. The decision was made to create the file from scratch rather than attempt partial recovery, ensuring clean, validated markup.

2. Content Structure and Decision Architecture

The proposal required careful information design to reduce decision fatigue, following established UX principles:

  • Pricing options: Two distinct tiers (Captain-only vs. full crew) with one explicitly recommended
  • Legal compliance: EPA/MPRSA ash scattering regulations and USCG licensing notes
  • Branding: Hollywood history context (Bogart, Bacall, Flynn, Wayne)
  • Conversion mechanism: Q&A form at page bottom for prospect engagement

This structure mirrors the card requirements while providing clear path-to-conversion without cognitive overload.

3. HTML Generation and Asset Integration

The proposal HTML file was written to:

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

Asset references were mapped to existing image infrastructure:

grep -n "img\|src=\|IMG_\|interior\|jpg\|png" /Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html

Image sources point to relative paths within /assets/images/interior/, ensuring compatibility with CloudFront's https://queenofsandiego.com/assets/images/interior/[filename] URL structure.

4. S3 Deployment

The file was deployed to S3 using environment-sourced credentials:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && \
aws s3 cp proposals/jada-charter-proposal-sue.html \
  s3://queenofsandiego.com/proposals/jada-charter-proposal-sue.html \
  --content-type "text/html; charset=utf-8"

The --content-type flag explicitly sets MIME type, preventing browser interpretation issues and ensuring proper caching headers propagate through CloudFront.

5. CloudFront Cache Invalidation

After S3 object creation, CloudFront cache invalidation was triggered to ensure all edge locations served the new content immediately:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && \
aws cloudfront create-invalidation \
  --distribution-id $CLOUDFRONT_DISTRIBUTION_ID \
  --paths "/proposals/jada-charter-proposal-sue.html"

The distribution ID and other credentials are stored in repos.env and referenced via environment variables. Invalidation scope targets only the specific object rather than wildcard patterns, minimizing unnecessary cache purges and associated processing costs.

6. Verification and Publication

Post-deployment verification confirmed object metadata and cache status:

set -a; source /Users/cb/Documents/repos/.secrets/repos.env; set +a && \
aws s3api head-object \
  --bucket queenofsandiego.com \
  --key proposals/jada-charter-proposal-sue.html

Live verification via curl confirmed content accessibility:

curl -s "https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html" | grep -E "charter|proposal"

Key Technical Decisions

Static Site Generation Over Dynamic Backend

This site uses pre-rendered static HTML rather than server-side templates. This approach eliminates runtime dependencies, reduces operational overhead, and enables aggressive CloudFront caching (cost efficiency and performance). Trade-off: content updates require repository commits and redeployment rather than database updates.

Environment Variable Sourcing Pattern

Using set -a; source [file]; set +a exports all variables in the secrets file without manually listing each one. This reduces shell script complexity but requires careful .env file management to avoid exposing unrelated credentials.

Targeted CloudFront Invalidation

Rather than invalidating broad paths like /proposals/*, the specific object path was invalidated. This reduces CloudFront processing load and avoids unnecessary cache purges for unrelated proposal files, important for sites with multiple dynamic proposal pages.

Explicit Content-Type Declaration

S3 object metadata defaults to application/octet-stream if not specified; explicit text/html; charset=utf-8 declaration ensures browsers parse as HTML and CloudFront applies correct caching policies.

Infrastructure Components Involved

  • S3 bucket: