Personal portfolio website - redesigned with a SecureByDefault dark theme and deployed on AWS S3 + CloudFront with automated GitHub Actions CI/CD.
Live at: ron-mercier101.com
A fully self-contained single-page portfolio built in vanilla HTML, CSS, and JavaScript - no frameworks, no dependencies, no build step. Deployed to AWS S3 via GitHub Actions on every push to main, served globally via CloudFront CDN with a custom domain and SSL/TLS certificate from ACM.
The site integrates a live visitor counter powered by a serverless backend: cloud-resume-backend - AWS Lambda + DynamoDB + API Gateway, deployed via AWS SAM.
| Component | Service | Purpose |
|---|---|---|
| Hosting | AWS S3 | Static file storage + website serving |
| CDN | AWS CloudFront | Global distribution, HTTPS enforcement, caching |
| Domain | AWS Route 53 | Custom domain DNS (ron-mercier101.com) |
| SSL/TLS | AWS ACM | Certificate management, auto-renewal |
| CI/CD | GitHub Actions | Automated S3 sync on push to main |
| Visitor counter | Lambda + DynamoDB + API GW | Serverless backend (separate repo) |
- Single-file architecture - all CSS and JavaScript inline in
index.html; no external dependencies - SecureByDefault dark theme -
#050C18navy,#00D4FFcyan accent, Syne display font, Space Mono monospace - Typewriter animation - cycles through 5 engineering titles in the hero section
- Animated skill bars - triggered by Intersection Observer when scrolled into view
- Fade-in sections - staggered reveal animations on scroll
- 6 pinned GitHub repos - project cards linking directly to repositories
- Contact Me button - opens user's default email client with
ron@securebydefault.iopre-loaded - Live visitor counter - footer counter calls the serverless backend API; animates count up on load
- Responsive - hamburger menu and mobile layout for all screen sizes
- Zero JavaScript frameworks - vanilla JS only, fast load, no bundle
AWS-S3-Static-Website/
├── index.html # Complete portfolio - HTML + CSS + JS (self-contained)
├── assets/
│ ├── banner.png # Repository banner image
│ └── architecture.png # AWS architecture diagram
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions CI/CD - S3 sync on push to main
├── .gitignore
└── README.md
- AWS account with appropriate IAM permissions
- AWS CLI configured locally
- Domain registered (Route 53 or external registrar)
- GitHub repository with Actions enabled
- Create an S3 bucket named to match your domain (e.g.,
ron-mercier101.com) - Enable Static website hosting → set
index.htmlas the index document - Configure bucket policy to allow CloudFront access (use Origin Access Control, not public ACLs)
- Create a CloudFront distribution pointing to your S3 bucket origin
- Set Origin Access Control (OAC) - do not make the bucket public directly
- Set Default root object to
index.html - Set Alternate domain names to your custom domain
- Attach your ACM certificate (must be in
us-east-1for CloudFront) - Set Price class appropriate for your audience
- Request a public certificate in us-east-1 (required for CloudFront)
- Add the CNAME validation records to Route 53
- Wait for status to show Issued
- Attach to your CloudFront distribution
- Create a hosted zone for your domain
- Add an A record (alias) pointing to your CloudFront distribution
- Add a AAAA record (alias) for IPv6 if desired
- Update your domain registrar to use Route 53 nameservers
The deploy workflow syncs index.html and assets to S3 on every push to main and optionally invalidates the CloudFront cache.
Create these GitHub Secrets/Variables:
AWS_S3_BUCKET- your S3 bucket nameAWS_CLOUDFRONT_DISTRIBUTION_ID- your CloudFront distribution ID- AWS credentials - either OIDC (recommended) or access key/secret pair
Recommended: use OIDC instead of stored AWS credentials. See the cloud-resume-backend repo for a detailed OIDC setup walkthrough.
The footer visitor counter calls the serverless API from cloud-resume-backend.
In index.html, locate:
const API_ENDPOINT = 'YOUR_API_ENDPOINT_HERE';Replace with your actual API Gateway endpoint:
const API_ENDPOINT = 'https://abc123.execute-api.us-east-1.amazonaws.com/Prod/count';Get the endpoint from SAM outputs after deploying the backend:
sam list stack-outputs --stack-name YOUR_STACK_NAMECORS note: The backend SAM template has CORS locked to https://ron-mercier101.com. The counter will only work from that origin. Local development will show — in the counter — this is expected and correct behavior.
Pushing to main triggers the GitHub Actions workflow automatically:
git add index.html
git commit -m "Update portfolio"
git push origin main
GitHub Actions syncs to S3. After sync, invalidate the CloudFront cache to see changes immediately:
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths "/*"Or add a cache invalidation step to your deploy.yml workflow so it runs automatically on every deploy.
Open index.html directly in a browser - no server required. Everything renders correctly locally except the visitor counter (CORS blocks the API call from file://).
# macOS / Linux
open index.html
# Windows
start index.htmlFor a local server (optional, resolves CORS for testing with a proxy):
python3 -m http.server 8080
# then visit http://localhost:8080| Token | Value | Usage |
|---|---|---|
--bg |
#050C18 |
Page background |
--card |
#0A1628 |
Card backgrounds |
--border |
#1A3A5C |
Borders, dividers |
--cyan |
#00D4FF |
Primary accent, links, highlights |
--head |
#EEF5FF |
Headings, primary text |
--body |
#8BB8D8 |
Body text |
--muted |
#4A7A9B |
Labels, secondary text |
--green |
#22D37E |
Visitor counter pulse dot |
| Display font | Syne 600/800 | Headings |
| Mono font | Space Mono | Code, labels, nav |
| Body font | DM Sans 300/400/500 | Body copy |
- cloud-resume-backend - Serverless visitor counter API (Lambda + DynamoDB + SAM)
- securebydefault-server-hardening - Linux server hardening configs
- cloud-security-checklist - 28-point security baseline
- ddos-tabletop-toolkit - DDoS IR tabletop exercises
- SecureByDefault.io - Security engineering blog
MIT License - feel free to fork and adapt for your own portfolio.
Built by Ron Mercier - Cloud & Cybersecurity Engineer. Previously: DDoS mitigation and incident response at Akamai Technologies. MSc Cybersecurity · CySA+ · PenTest+ · ISC2 CC · AWS CCP.

