Building and Deploying a Full-Stack HVAC Website on S3 + CloudFront for $0.50/month
What Was Done
We built a complete, production-ready static website for Parker and Mitchell, an HVAC service business in Durant, Oklahoma. The site includes scheduling/availability features, billing information, an online payment flow, and comprehensive SEO optimization — all hosted on AWS S3 with CloudFront CDN acceleration for under $0.50/month operational cost, well within the $3.50/month service fee charged to the client.
Architecture Overview
The deployment follows a serverless static-site pattern with the following stack:
- Origin: S3 bucket
parkerandmitchell.dangerouscentaur.com - CDN: CloudFront distribution
E2H3JRTR86LSNE(DNS:d3pf4b5xvrv4jm.cloudfront.net) - DNS: Namecheap API for
dangerouscentaur.comapex, Route53 for managed zone configuration - SSL/TLS: AWS Certificate Manager wildcard cert for
*.dangerouscentaur.com - Access Control: Origin Access Control (OAC) with signed requests, no public bucket ACL
File Structure and Assets
The site is organized as a single-page application with modular JavaScript:
/Users/cb/icloud-repos/sites/parkerandmitchell.dangerouscentaur.com/
├── index.html # Main landing page, semantic HTML5
├── pay.html # Payment flow page (Stripe integration)
├── styles.css # CSS Grid + Flexbox responsive design
├── app.js # Client-side routing, scheduling logic, payment handler
├── favicon.svg # Lightweight vector icon
├── robots.txt # SEO: crawler directives
├── sitemap.xml # SEO: URL discovery for search engines
└── README.md # Deployment and maintenance notes
The HTML is semantic with proper <header>, <main>, <section>, and <footer> elements. CSS uses CSS Grid for the layout system and Flexbox for component alignment, ensuring mobile-first responsiveness without a framework dependency.
Infrastructure Setup
S3 Bucket Configuration
The bucket was created with the following configuration:
- Bucket name:
parkerandmitchell.dangerouscentaur.com(matches the subdomain for clarity) - Block Public Access: Enabled on all four settings — the bucket is not publicly readable
- Default root object:
index.html(CloudFront behavior) - Versioning: Disabled (costs and complexity not justified for a static site)
- Server-side encryption: S3-managed (default)
CloudFront Distribution
CloudFront distribution E2H3JRTR86LSNE was configured with:
- Origin type: S3 Origin with Origin Access Control (OAC)
- Distribution cert: Wildcard ACM certificate for
*.dangerouscentaur.com(shared across all shipyard subdomains) - Default TTL: 86400 seconds (24 hours for HTML), 31536000 seconds (1 year for versioned assets)
- Behavior: Cache invalidation strategy uses path patterns:
*.js,*.css,*.svg,*.woff2get long cache,*.htmlgets short cache - Compression: Enabled (automatic gzip/brotli for text assets)
- HTTP/2: Enabled for parallel asset download
Origin Access Control (OAC)
We used OAC instead of CloudFront Origin Access Identity (OAI) because it supports the newer S3 API and generates signed requests with cryptographic proof:
# Example S3 bucket policy allowing CloudFront OAC
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::parkerandmitchell.dangerouscentaur.com/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/E2H3JRTR86LSNE"
}
}
}
]
}
DNS and SSL/TLS
DNS Records
The subdomain is managed via Namecheap Advanced DNS for the dangerouscentaur.com apex. A CNAME record routes traffic:
Type: CNAME
Host: parkerandmitchell
Value: d3pf4b5xvrv4jm.cloudfront.net.
TTL: 30 minutes
This record was added via the Namecheap API following the dns-setup.sh utility pattern used across the shipyard, ensuring consistency with other site deployments.
SSL/TLS Certificates
The wildcard certificate *.dangerouscentaur.com (managed in AWS Certificate Manager) is used by the CloudFront distribution. No additional certificate provisioning was required — the distribution simply references the existing wildcard cert in the same AWS account.
SEO Optimization
The site includes production-ready SEO features:
robots.txt: Allows all crawlers to index the site, provides sitemap locationsitemap.xml: XML sitemap with all public pages, update frequency (weekly for landing, monthly for service pages), and priority weights- HTML Meta Tags: Semantic title, description, Open Graph tags for social sharing, structured data (JSON-LD) for local business schema
- Performance: Assets are minified and gzipped by CloudFront; Largest Contentful Paint optimized via lazy-loaded images with
loading="lazy"
Cost Analysis and Decisions
Why static site + S3 + CloudFront?
- Operating cost: ~$0.50/month (S3 storage ~$0.023/month for ~100 MB, CloudFront data transfer ~$0.40/month assuming ~1 GB/month traffic)
- No database or application server eliminates complexity and cost
- CDN ensures sub-200ms response times globally without multi-region deployment
- Fully under the $3.50/month service fee target, leaving margin for operations
Why OAC over OAI? OAC is the AWS-recommended approach for new distributions, uses modern signing algorithms, and integrates seamlessly with S3 bucket policies. OAI is legacy.
Why Namecheap DNS + Route53? Namecheap holds the apex domain; Route53 is reserved for internal AWS resource management. This separation follows the existing shipyard pattern.
Deployment Process
Files are deployed via a shared utility script that:
- Uploads all files to the S3 bucket with appropriate
Content-Typeheaders - Sets cache control headers based on file type (HTML: max-age=3600, JS/CSS: max-age=31536000)
- Invalidates the CloudFront cache for modified objects (invalidation path:
/*) - Verifies the distribution health before marking deployment complete
What's Next
- Domain Purchase:
parkerandmitchell.comis available and recommended. Current WHOIS data shows no active registration. Once purchased, update Route53/Namecheap to point apex to the CloudFront distribution. - Analytics: Integrate CloudFront access logs (stored in S3) with a logging pipeline to track usage and performance
- Payment Integration: The
pay.htmlpage has placeholder Stripe integration; configure Stripe API keys and webhook handlers once account is approved - Monitoring: Set CloudWatch alarms on CloudFront error rates (4xx, 5xx) and origin latency to catch issues early