Multi-Stage CloudFront Deployment Pipeline: Implementing Tracked Job Creation and URL Rewriting for QuickDumpNow
Overview
This post details the production deployment of QuickDumpNow's dispatch tracking system, including infrastructure updates to handle URL rewriting, job creation with tokenized tracking links, and multi-environment promotion workflows. The deployment involved coordinating CloudFront function updates, S3 object uploads, cache invalidations, and real-time job status tracking across staging and production environments.
What Was Done
The core objective was to enable job creation and tracking for a customer (Mark) with full end-to-end visibility:
- Updated CloudFront function to handle
/book/URL rewrites alongside existing/track/rewrites - Generated unique job IDs and tracking tokens for new jobs
- Persisted job data to S3-backed jobs.json
- Promoted staging versions of dashboard, track page, and booking pages to production
- Invalidated CloudFront caches across multiple distributions
- Verified end-to-end tracking link functionality
Technical Architecture
URL Rewriting via CloudFront Functions
The primary infrastructure pattern relies on CloudFront Functions for lightweight request/response manipulation. Rather than routing through Lambda@Edge or origin servers, we use a viewer-request function to rewrite URLs before they hit the origin.
File: /Users/cb/Documents/repos/sites/quickdumpnow.com/cf/qdn-track-rewrite.js
The function was updated to handle both /track/{token} and /book/ routes:
// CloudFront Function pattern for path-based routing
// Rewrites /track/TOKEN → /track/index.html
// Rewrites /book/* → /book/index.html
// Preserves query strings and maintains browser history
This approach avoids origin redirects (reducing latency), maintains clean URLs for users, and centralizes routing logic in infrastructure-as-code.
Job Persistence: S3 as State Backend
Jobs are persisted as JSON objects in S3, serving as the single source of truth:
- Bucket: quickdumpnow.com (inferred from domain)
- Object Key:
jobs.json - Structure: Array of job objects, each containing job ID, customer name, address, tracking token, status, and timestamps
The workflow retrieves the current jobs.json, appends Mark's new job record, and re-uploads with cache-control headers to ensure immediate propagation.
Tracking Token Generation
Job tracking tokens are cryptographically unique identifiers enabling customer access to job status without authentication:
// Token generation process
Generate job ID (e.g., "JOB_20240523_001")
Generate tracking token (hex-encoded random bytes)
Create job object: {jobId, name: "Mark", address: "Soderblom ave", token, status: "booked", createdAt, ...}
Upload to jobs.json
Construct tracking URL: https://quickdumpnow.com/track/{token}
The tracking page (/track/index.html) extracts the token from the URL via CloudFront rewrite, fetches jobs.json, and locates the matching job by token to display status and details.
Infrastructure Components and Deployment Flow
CloudFront Distribution Configuration
Two separate CloudFront distributions handle different purposes:
- Main distribution: Serves quickdumpnow.com content (dashboard, track pages, booking pages)
- Staging distribution: Pre-production testing environment before promotion to main
Cache invalidation was performed on both distributions post-deployment:
// Invalidate specific paths after updates
aws cloudfront create-invalidation \
--distribution-id {DISTRIBUTION_ID} \
--paths "/track/*" "/book/*" "/dashboard*" \
--profile quickdumpnow
Invalidating /track/* and /book/* ensures all token-based URLs are refetched from origin, preventing stale content from being served to customers with tracking links.
S3 Object Versioning and ETags
The CloudFront function file itself is version-controlled with ETags to prevent race conditions during updates:
// Fetch current ETag before updating CF function
aws cloudfront get-function \
--name qdn-track-rewrite \
--stage DEVELOPMENT \
--profile quickdumpnow
// Update with IfMatch condition
aws cloudfront update-function \
--name qdn-track-rewrite \
--function-code fileb://qdn-track-rewrite.js \
--function-config Comment="Handle /track/ and /book/ rewrites" \
--if-match {ETAG} \
--stage DEVELOPMENT
The ETag ensures the function update is atomic; if another deployment occurs concurrently, the conditional update fails with a 409 Conflict, preventing overwrite bugs.
Multi-Stage Promotion Pattern
The deployment follows a strict progression:
- Development/Staging Stage: Upload and test new CF function code
- Publish to LIVE: Promote CloudFront function from DEVELOPMENT to LIVE
- S3 Staging → S3 Production: Copy updated pages (dashboard, track page, booking pages) from staging prefix to production prefix
- Cache Invalidation: Invalidate affected paths on production distribution to force refresh
This separation ensures the CF function (stateless, lightweight) and S3 objects (stateful content) are deployed independently, reducing blast radius if either update has issues.
Job Creation and Status Flow
Once Mark's job was created in jobs.json with initial status "booked", the workflow updated it to "ready_for_pickup" to reflect his message that the trailer is ready this afternoon.
The dashboard and track page reference a shared STATUS_FLOW definition that maps valid status transitions:
booked→ customer has scheduled a pickupready_for_pickup→ customer confirms trailer is readyin_progress→ pickup crew is en route or actively dumpingcompleted→ job finished, payment processed
Status badges and UI pills render according to this flow, ensuring customers see consistent, sequential updates on both the public track page and internal dashboard.
Verification and Error Handling
The initial error message "Job not found, or this tracking link is no longer valid" indicated that the track page couldn't locate Mark's job in jobs.json. This was resolved by:
- Confirming jobs.json was properly uploaded with Mark's record
- Testing the tracking URL directly:
https://quickdumpnow.com/track/{token} - Verifying the CloudFront function correctly rewrote the path to
/track/index.html