Deploying a Dynamic Proposal Template System: From Git to CloudFront CDN
What Was Done
Created and deployed a fully-functional proposal page for a charter service at queenofsandiego.com/proposals/jada-charter-proposal-sue.html. This involved:
- Generating a single-source-of-truth HTML proposal template with embedded styling
- Populating dynamic content (pricing, legal compliance, service options)
- Uploading to S3 bucket and invalidating CloudFront distribution
- Verifying live deployment with curl and browser validation
The page had existed only as a template stub; this deployment marked its first production appearance.
Technical Details
File Structure & Git Management
The proposal file lives in the static site repository:
/Users/cb/Documents/repos/sites/queenofsandiego.com/proposals/jada-charter-proposal-sue.html
This path maps to a flat file structure where each proposal is a self-contained HTML document. The directory structure follows:
proposals/— all proposal documentsassets/images/— shared image resourcespublish_static_site.sh— deployment orchestration script
The workflow involved multiple rapid edits (18+ commits in a single session) to refine content, validate pricing, and ensure legal compliance language was present before publishing.
Content Architecture
The proposal page implements a decision-minimization pattern: exactly two options presented with one explicitly recommended. This reduces choice paralysis—a documented UX anti-pattern in high-value sales scenarios.
- Option A (Recommended): 2-hour charter, Captain Sergio only — $750 flat
- Option B: 3-hour charter, full crew — $1,575 flat
Supporting content includes:
- Legal compliance section (USCG licensing, EPA/MPRSA ash scattering regulations)
- Historical context (Hollywood maritime history references)
- Q&A form for customer inquiries
All styling is embedded via inline <style> tags to ensure the document renders correctly even if external CSS fails to load—critical for a proposal document sent via email or viewed on unstable connections.
Infrastructure & Deployment Pipeline
S3 Upload
The file was uploaded to the static site's S3 bucket using AWS CLI with environment credentials sourced from a secrets file:
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
The set -a / set +a pattern ensures all exported variables from the secrets file are available to the AWS CLI command without polluting the shell environment permanently.
CloudFront Cache Invalidation
After S3 upload, the CloudFront distribution cache was invalidated to force edge nodes to fetch the latest version:
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/proposals/jada-charter-proposal-sue.html"
The distribution ID is stored in the secrets file and sourced at runtime. Invalidation typically propagates globally within 60 seconds, though edge node updates may take up to 5 minutes to fully converge.
Verification
Live deployment was verified using curl to check HTTP response headers and grep to validate content presence:
curl -s "https://queenofsandiego.com/proposals/jada-charter-proposal-sue.html" | grep -E "Option A|$750|Captain"
This confirms the page is publicly accessible, served from CloudFront (via HTTP headers), and contains expected content.
Key Architectural Decisions
Self-Contained HTML vs. Template System
The proposal is a single HTML file rather than a template that's dynamically rendered. Why: Proposals are static, rarely-changing documents. A template system adds complexity (server-side rendering, template language, variable injection) without benefit. A self-contained file:
- Renders identically in all browsers (no JavaScript required)
- Survives email forwarding and Slack/Teams sharing without degradation
- Has zero runtime dependencies
- Versions cleanly in Git with full content visibility
Embedded CSS Over External Stylesheets
All styling lives in <style> tags within the document. Why: A proposal document is often printed, emailed, or viewed offline. External CSS files may fail to load, resulting in an unstyled page. Embedded CSS guarantees consistent presentation in all contexts.
CloudFront CDN for Proposals
Even static proposal documents are served through CloudFront (not directly from S3). Why:
- Origin Shield: Protects S3 from traffic spikes
- TTL Control: Allows fine-grained cache expiration (important if pricing changes)
- HTTPS: CloudFront front-end enforces HTTPS; S3 direct access is less secure
- Geographic Distribution: Even a static document loads faster from edge locations near the user
Secrets Management Pattern
AWS credentials are stored in /Users/cb/Documents/repos/.secrets/repos.env and sourced at command time using set -a. Why:
- Credentials never appear in shell history
- Environment variables are scoped to the single command
- The secrets file is Git-ignored, preventing accidental commits
- Rotation is simple: update the secrets file, re-source, deploy
What's Next
- Image Integration: The proposal references images (vessel photos, interior shots, historical images). Once image files are provided with exact paths, they will be copied to
assets/images/and referenced via relative paths in the HTML. - Contact Information Cleanup: Carole's name and personal email will be replaced with a shared booking contact (
bookings@queenofsandiego.com). - A/B Testing: Once the page is live with customers, consider deploying variant versions (e.g., different pricing options, messaging) via CloudFront function-based request routing to measure conversion rates.
- Pricing Validation: Cross-reference the $750 and $1,575 figures with business logic to confirm accuracy. The card notes suggested these may have been truncated in transit.
Deployment Summary
The proposal page is now live at https://