Astro project setup and Cloudflare Pages deployment.
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 20+ | JavaScript runtime |
| npm | 9+ | Package manager |
| Git | Any recent | Version control |
| Wrangler CLI | Latest | Cloudflare Pages deployment (optional — can use dashboard) |
- Cloudflare — free tier account for Pages deployment
- GitHub — repo is at
houseofcwk/cwk-plos-site
When ready to build, scaffold the Astro project:
cd projects/cwk-plos-site
npm create astro@latest . -- --template minimal --typescript strictnpx astro add cloudflare # Cloudflare Pages adapter
npx astro add react # React for interactive islands (Brand Mirror quiz)
npx astro add sitemap # Auto-generated sitemap.xmlastro.config.mjs should include:
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://cwkexperience.com',
output: 'static',
adapter: cloudflare(),
integrations: [
react(),
sitemap(),
],
});Note: Use
output: 'static'for fully static generation. Switch tooutput: 'hybrid'only if specific pages need SSR (e.g., dynamic Brand Mirror results).
npm install
npm run dev # http://localhost:4321| Command | Purpose |
|---|---|
npm run dev |
Start Astro dev server with HMR |
npm run build |
Build for production → dist/ |
npm run preview |
Preview production build locally |
npm run astro check |
TypeScript type checking |
- Go to dash.cloudflare.com → Workers & Pages → Create
- Select Connect to Git → authorize GitHub → select
houseofcwk/cwk-plos-site - Configure build:
| Setting | Value |
|---|---|
| Framework preset | Astro |
| Build command | npm run build |
| Build output directory | dist |
| Root directory | / (project root) |
| Node.js version | 20 |
- Add environment variables:
| Variable | Value |
|---|---|
PUBLIC_SITE_URL |
https://cwkexperience.com |
PUBLIC_CF_ANALYTICS_TOKEN |
(from Cloudflare Web Analytics) |
NODE_VERSION |
20 |
- Click Save and Deploy
Every push to main will auto-deploy.
npm run build
npx wrangler pages deploy dist --project-name=cwk-plos-site- In Cloudflare Pages project settings → Custom domains
- Add
cwkexperience.comandwww.cwkexperience.com - If the domain's DNS is already on Cloudflare, CNAME records are auto-configured
- If not, add these DNS records:
CNAME cwkexperience.com cwk-plos-site.pages.dev
CNAME www cwk-plos-site.pages.dev
- Enable Always Use HTTPS in Cloudflare dashboard
- Set up a page rule or redirect:
www.cwkexperience.com/*→https://cwkexperience.com/$1(301)
- Go to Cloudflare dashboard → Web Analytics → Add site
- Copy the beacon token
- Set as
PUBLIC_CF_ANALYTICS_TOKENin environment variables - Add the analytics script in
Base.astrolayout:
<!-- Cloudflare Web Analytics -->
<script
defer
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "{PUBLIC_CF_ANALYTICS_TOKEN}"}'
></script>This is cookie-free, privacy-first analytics. No consent banner needed.
Create public/_redirects for old URL compatibility:
/aboutkris /about 301
/krisjourneyarticle /about 301
/flexlink /work 301
/thelabmiamicampusarticle /work/the-lab-miami 301
/robdialarticle /work/rob-dial 301
/spraycationmuraltour /work/spraycation 301
/dawaarticle /work/dawa 301
/stephyleearticle /work/stephy-lee 301
/raasininthesunarticle /work/raasin-in-the-sun 301
/beatstarsarticle /work/pay-the-creators 301
/lifestapestryarticle /work/lifes-tapestry 301
/brandmirrorlobby /brand-mirror 301
/brandmirror /brand-mirror 301
/sidequests /side-quests 301
Cloudflare Pages reads _redirects from the output directory automatically.
| Metric | Target |
|---|---|
| Lighthouse Performance | 95+ |
| First Contentful Paint | < 1.0s |
| Largest Contentful Paint | < 1.5s |
| Total Blocking Time | < 100ms |
| Cumulative Layout Shift | < 0.05 |
| Time to First Byte | < 100ms (edge) |
Astro's zero-JS default + Cloudflare's edge CDN should achieve these easily for all static pages. The Brand Mirror quiz page will have slightly higher TBT due to React hydration.
-
sitemap.xmlauto-generated by@astrojs/sitemap -
robots.txtinpublic/allowing all crawlers - Unique
<title>and<meta name="description">per page - Open Graph tags on every page
- Canonical URLs on every page
- JSON-LD structured data on homepage (Organization) and case studies (Article)
-
_redirectsfile for old URL compatibility (301s) - All images have
alttext and use Astro's<Image>for optimization -
<html lang="en">set
Before going live:
- All pages render correctly with production build (
npm run build && npm run preview) - Brand Mirror quiz works end-to-end
- All old URLs redirect correctly (test each 301)
- Custom domain configured and SSL active
- Cloudflare Web Analytics token set
- OG images render correctly when shared on social media
- Mobile responsive on all pages (test 375px, 768px, 1024px)
- Favicon and apple-touch-icon configured
-
robots.txtandsitemap.xmlaccessible - Page speed audit passes targets above
Go to https://resend.com and create an account.
In Resend dashboard: Domains → Add domain → cwkexperience.com
Add the DNS records Resend provides (SPF, DKIM, DMARC).
Resend → API Keys → Create API Key → name it "cwk-plos-production" Save the key — you will only see it once.
APIs now live in workers/api/ (a single consolidated worker —
cwk-api-prod — at api.cwkexperience.com). Only RESEND_API is a secret;
the rest are plain vars in workers/api/wrangler.toml.
cd workers/api
wrangler secret put RESEND_API --env production
# Optional — only if you want contact-form spam defenses on full strength:
wrangler secret put TURNSTILE_SECRET --env production
wrangler secret put HASH_SALT --env productionValues:
RESEND_API: your Resend API key (re_...)TURNSTILE_SECRET: Cloudflare Turnstile site secret (for contact form)HASH_SALT: any random string; salts the SHA-256 IP hash used by the contact rate limiter. Rotating it invalidates existing rate-limit keys.
The FROM_EMAIL, FROM_NAME, REPLY_TO_EMAIL, REPLY_TO_NAME,
CONTACT_INBOX_TO values are plain [vars] / [env.production.vars] in
workers/api/wrangler.toml — edit the file and redeploy, no secret step
needed.
Run the Astro dev server and the API worker independently:
# terminal 1 — static site
npm run dev
# terminal 2 — API worker with local KV
cd workers/api
wrangler dev