IT 시행착오··약 8분

Cloudflare Workers에 정적 사이트 올리는 법

Astro 정적 블로그를 wrangler assets로 Workers에 올리는 실제 설정과, workers.dev는 되는데 커스텀 도메인만 막히던 경험을 정리합니다.

Cloudflare Workers 배포 — Cloudflare office entrance (cc by-sa 4.0, wikimedia-commons)

Workers를 API 전용으로만 쓰는 줄 알았는데, 정적 블로그도 잘 올라갑니다. 서민혁닷컴은 Astro로 dist를 만들고, wrangler.jsonc의 assets에 그 폴더를 물려 warpsense-blog라는 Worker 이름으로 배포했습니다. (공개 브랜드와 Worker 이름이 달라도 됩니다. 배포 식별자일 뿐입니다.)

핵심은 소스(src/)가 아니라 빌드 결과를 올리는 것입니다.

지금 쓰는 최소 설정

대략 이런 형태입니다.

{
  "name": "warpsense-blog",
  "compatibility_date": "2026-07-01",
  "assets": {
    "directory": "./dist",
    "html_handling": "drop-trailing-slash",
    "not_found_handling": "404-page"
  }
}

로컬에서는:

npm install
npm run build
npx wrangler deploy

배포가 끝나면 https://<이름>.<계정>.workers.dev로 바로 확인합니다. 이쪽은 대체로 막힘없이 HTML이 내려옵니다. 문제가 생기면 십중 아홉은 directory 경로 오타거나, 빌드 전에 deploy를 돌린 경우입니다. dist/index.html이 있는지부터 봅니다.

html_handling: drop-trailing-slash/about//about을 한쪽으로 맞출 때 편합니다. canonical·sitemap과 규칙을 같게 두세요. not_found_handling: 404-page는 없는 글 슬러그에서 커스텀 404를 보여 줍니다.

배포 전에 내가 실패했던 것

  • wrangler deploy만 하고 npm run build를 안 함 — 어제 dist가 올라갑니다. 새 글이 “안 보이는” 증상이 나옵니다.
  • directory./src나 프로젝트 루트로 둠 — 마크다운·설정이 그대로 노출되거나, 진입 HTML이 없습니다.
  • 로컬에 dist/index.html이 있는데 배포 후 404 — 계정·Worker 이름·다른 프로젝트의 wrangler 설정을 보고 있었거나, 토큰 계정이 달랐습니다.
  • trailing slash 규칙과 Astro 링크가 불일치 — 어떤 링크는 /about/, sitemap은 /about이면 리다이렉트·중복이 납니다. 한쪽으로 고정합니다.

확인 루틴:

test -f dist/index.html && echo "dist ok" || echo "build first"
npx wrangler deploy
curl -sI "https://warpsense-blog.<계정>.workers.dev/" | head -n 12

workers.dev에서 200이 나오면 “빌드·assets·배포” 층은 통과입니다. 그다음 커스텀 도메인으로 갑니다.

커스텀 도메인을 붙일 때

Workers 대시보드에서 Custom Domain으로 apex를 붙이면 DNS에 AAAA 100::(프록시) 같은 레코드가 생깁니다. HTTPS는 Cloudflare가 처리합니다.

여기서 한 번 크게 헷갈렸습니다. workers.dev는 200인데 seominhyuk.com만 403 Challenge. 배포가 잘못된 게 아니라 존(Bot Fight) 설정이 HTML 응답 앞에 끼어든 거였습니다. 정적 assets의 ads.txt·robots.txt는 캐시 HIT로 200이 나오는데, HTML·sitemap만 챌린지되는 패턴이라 더 헷갈렸습니다. 도메인 연결 체크는 HTTPS·커스텀 도메인 DNS에, 챌린지 해제는 Bot Fight 글에 적어 두었습니다.

www를 쓰려면 Custom Domain이 API 권한 때문에 안 붙을 때가 있습니다. 그때는 wwwAAAA 100::를 만들고 Workers Route www.도메인/* → 스크립트 이름으로 연결하는 식으로 우회했습니다.

배포 ≠ 크롤 가능 검증

커스텀 도메인을 붙인 뒤에는 사람 브라우저만 보지 않습니다.

curl -sI -A "AdsBot-Google (+http://www.google.com/adsbot.html)" \
  https://seominhyuk.com/ | head -n 15

cf-mitigated: challenge가 보이면 wrangler 설정을 더 만지지 않습니다. 존 보안 쪽입니다. 반대로 workers.dev만 보고 “배포 완료”라고 끝내면, AdSense·Search Console에서만 실패가 납니다.

캐시는 배포 직후 옛 HTML이 남을 수 있습니다. 이상하면 존 캐시 퍼지 후 시크릿 창으로 다시 봅니다. “글 내용이 안 바뀌었다”고 assets 경로를 의심하기 전에, 퍼지와 curl -sIcf-cache-status를 먼저 봅니다.

Pages랑 뭐가 다르냐

같은 계정에서 Pages + Worker API를 같이 돌리는 프로젝트(리뷰올 등)도 있습니다. 블로그만이면 Workers assets 하나로도 충분하고, Pages 깃 연동 + API Worker 분리는 Pages와 Workers를 같이 배포할 때에 노션에 정리해 두었던 명령 흐름을 바탕으로 적어 두었습니다.

트레이드오프는 단순합니다. Workers assets는 설정이 적고, “빌드 산출물 업로드”에 가깝습니다. Pages는 깃 연동·프리뷰 URL이 편하지만, 블로그만 운영할 때는 레이어가 하나 더 생깁니다. 저는 블로그는 Workers로 고정하고, API가 필요한 다른 서비스만 Pages/Worker 분리를 씁니다.

마크다운으로 글을 쌓는 흐름 자체는 마크다운 기술 블로그에 맞춰 두었습니다. 배포 파이프라인은 “글 작성 → build → wrangler → workers.dev 확인 → 커스텀 도메인·봇 curl” 순서를 깨지 않는 게 핵심입니다.

CI에서 CLOUDFLARE_API_TOKEN이 없을 때는 로컬 deploy에 의존하지 말고, GitHub Actions 배포 워크플로가 돌게 두는 편이 안전합니다. 토큰은 저장소 secrets에만 두고, 로그에 찍히지 않게 합니다. 키 관리 감각은 API 키·env와 같습니다.

배포 직후 한 줄 습관

배포가 끝나면 로그의 “Uploaded”만 보고 끝내지 않습니다. workers.dev 헤더, 커스텀 도메인 헤더, AdsBot 헤더를 짧게라도 비교합니다. 셋이 갈라지면 어느 층(빌드·DNS·Bot Fight·캐시)인지 바로 갈립니다.

정리하면, 정적 블로그는 buildassets.directorywrangler deploy → workers.dev 확인 → 커스텀 도메인 → 봇이 HTML을 받는지 순서로 보면 헛수고가 줄어듭니다. workers.dev 200을 “전부 정상”으로 읽지 않는 것이 이 글의 전부입니다.