Generates daily visitor and top pages charts from CloudFront JSON logs.
Outputs JSON files to your static site S3 bucket, rendered by Chart.js on your about page.
EventBridge (daily 06:00 UTC)
└─ Lambda (Node.js 22)
├─ Reads last 30 days of CloudFront .gz logs from S3
├─ Filters bots, probes, assets
├─ Writes visitors.json + pages.json to website S3
└─ Invalidates CloudFront cache
- AWS CLI configured (
aws configure) - SAM CLI installed (
pip install aws-sam-cli)
sam build
sam deploy --guidedOn first deploy, SAM will prompt for:
| Parameter | Value |
|---|---|
LogsBucket |
Your CloudFront logs bucket name |
LogsPrefix |
Key prefix for logs (empty if at root) |
WebsiteBucket |
Your website S3 bucket name |
DistributionId |
Your CloudFront distribution ID |
SAM saves your answers in samconfig.toml for subsequent deploys (sam deploy without --guided).
sam build
sam local invoke AnalyticsFunctionaws lambda invoke \
--region eu-west-3 \
--cli-read-timeout 0 \
--function-name $(sam list stack-outputs --stack-name daily-statistics --region eu-west-3 --output json | jq -r '.[] | select(.OutputKey=="FunctionName") | .OutputValue') \
/dev/stdoutCopy the contents of chart-snippet.html into your about.md (or about.html) page.
Chart.js is loaded from cdnjs — no cookies, no tracking, consistent with your privacy policy.
| Resource | Cost |
|---|---|
| Lambda (1 run/day, ~30s, 512MB) | ~$0.00 |
| S3 reads (log files) | ~$0.01 |
| CF invalidation (1/day) | $0.00 |
| Total | ~$0 |
The Lambda excludes:
- Known bots (Googlebot, GPTBot, SEMrush, Ahrefs, etc.)
- Empty / missing user agents
- HEAD requests
- PHP/WordPress probes (wp-login, xmlrpc, .env, etc.)
- Static assets (CSS, JS, images, fonts, XML, JSON)
Only GET requests returning 2xx/3xx for HTML pages are counted.
Unique visitors = unique IPs per day.