Cloudflare Pages는 정적 파일 폴더를 통째로 받아 전 세계에 뿌려주는 무료 호스팅입니다. 명령 한 줄이면 배포가 끝납니다.
무료 한도는 배포당 파일 2만 개, 파일 하나당 25MiB입니다. 이 블로그는 2,441개로 12%를 씁니다.
배포는 쉽지만 그 뒤가 함정입니다 — HTML은 엣지에 캐시되지 않고, www는 자동으로 안 붙고, pages.dev 주소도 같이 공개됩니다.
지난 편에서 워드프레스를 폴더 하나로 내보내는 것까지 했습니다. 이제 그 폴더를 인터넷에 올릴 차례입니다 — Cloudflare Pages 무료 배포입니다.
Cloudflare Pages가 뭔가요?
Cloudflare Pages는 정적 파일 폴더를 받아서 전 세계 서버에 복사해두고, 방문자에게 가장 가까운 곳에서 내보내주는 서비스입니다. 서버를 빌리는 게 아니라 파일을 맡기는 쪽에 가깝습니다.
핵심은 무료 범위가 개인 블로그엔 과할 만큼 넓다는 점입니다.
| 항목 | 무료 플랜 한도 | 이 블로그 |
|---|---|---|
| 배포당 파일 수 | 20,000개 | 2,441개 (12%) |
| 파일 하나 크기 | 25MiB | 최대 3.8MB |
| 커스텀 도메인 | 프로젝트당 100개 | 1개 |
| 월 빌드 횟수 | 500회 (Git 연동 빌드에만 해당) | 해당 없음 |
마지막 행을 짚고 넘어가겠습니다. 월 500회 빌드 한도는 Git 저장소를 연결해 Cloudflare가 대신 빌드해줄 때 쓰는 숫자입니다. 이 글처럼 이미 만들어진 폴더를 직접 올리는 방식(Direct Upload)은 Cloudflare가 빌드를 돌릴 게 없어서 이 한도를 쓰지 않습니다. 배포 횟수를 세고 있을 필요가 없다는 뜻입니다.
파일 2만 개는 넉넉해 보이지만 워드프레스는 생각보다 파일을 많이 뱉습니다. 다만 그 대부분이 글 수와 무관한 고정분입니다. 2,441개 중 1,826개(75%)가 워드프레스 코어 자산(wp-includes)이고, 글 한 편이 늘 때 실제로 추가되는 건 40개 남짓입니다. 지난 편을 쓸 때 2,391개였던 게 글 두 편이 더해지자 2,441개가 됐습니다. 글이 100편이 돼도 7천 개 언저리라 한도는 한참 남습니다.
배포는 정말 한 줄인가요?
네. 폴더와 프로젝트 이름만 있으면 됩니다.
npx -y wrangler pages deploy build --project-name=aimakelog --branch=main
wrangler는 Cloudflare의 공식 CLI입니다. 처음 쓸 때 npx -y wrangler login으로 브라우저 로그인을 한 번 하면, 이후로는 계정 인증이 유지됩니다.

주목할 부분은 “Uploaded 0 files (2416 already uploaded)” 입니다. 매번 수천 개를 통째로 올리는 게 아니라 바뀐 파일만 올립니다. 글 한 편을 새로 넣은 직전 배포에서는 44개만 올라갔고, 그마저 1.5초에 끝났습니다.
배포마다 71114403.aimakelog.pages.dev 같은 고유 주소가 생깁니다. 이전 배포가 그대로 살아 있다는 뜻이라, 뭔가 잘못되면 대시보드에서 이전 배포로 되돌릴 수 있습니다.
내 도메인은 어떻게 붙이나요?
Cloudflare에서 도메인을 샀다면(이 블로그는 Cloudflare Registrar) 대시보드에서 Pages 프로젝트에 커스텀 도메인을 추가하는 것으로 끝납니다. DNS 레코드와 SSL 인증서가 자동으로 붙습니다.
붙고 나면 DNS가 이렇게 보입니다. CNAME이 아니라 A 레코드입니다.
$ dig +short aimakelog.app
104.21.19.12
172.67.184.111
이유가 둘 겹쳐 있습니다. 하나는 루트 도메인(aimakelog.app)에는 원래 CNAME을 쓸 수 없다는 DNS 규칙이라, Cloudflare가 CNAME을 조회 시점에 펴서 A처럼 응답한다는 것(CNAME 플래트닝). 다른 하나는 이 레코드가 Cloudflare 프록시를 거치고 있어서, 돌아오는 주소가 내 서버가 아니라 Cloudflare 자기네 IP라는 것입니다. 직접 설정할 건 없지만, DNS를 열어보고 “왜 CNAME이 아니지?” 할 때 알고 있으면 편합니다.
배포 뒤에 알게 되는 함정 4개

함정 ① 없는 주소가 200을 돌려준다
가장 조용하고 가장 아픈 함정입니다. 404.html이 없으면 Pages는 없는 주소에 대해 홈 화면을 200으로 내보냅니다.
검색엔진은 이걸 소프트 404로 보고, 오타 주소나 죽은 링크가 전부 색인 후보가 됩니다. build/404.html을 넣으면 Pages가 알아서 그 파일을 404 상태로 내보냅니다. 위 화면의 마지막 줄이 넣고 난 뒤의 결과입니다. 이 파일을 만드는 방법은 지난 편에 적어뒀습니다.
함정 ② HTML은 엣지에 캐시되지 않는다
Cloudflare Pages는 이미지·CSS·JS는 엣지에 캐시하지만 HTML은 기본적으로 캐시하지 않습니다. 위 화면의 첫 두 명령이 그 차이입니다.
# HTML
cache-control: public, max-age=0, must-revalidate
cf-cache-status: DYNAMIC
# 이미지
cache-control: public, max-age=14400, must-revalidate
cf-cache-status: REVALIDATED
DYNAMIC은 “엣지 캐시를 안 탄다”는 뜻입니다. 글을 고쳐 배포하면 즉시 반영되는 게 이 덕분입니다.
그럼 느려지느냐 — 재봤더니 그렇지도 않았습니다. 제 맥에서 다섯 번씩 재보니 첫 바이트까지 걸린 시간이 HTML은 평균 0.55초, 엣지 캐시를 타는 이미지는 0.46초였습니다. 그런데 그중 0.3초가량은 양쪽 모두 연결을 맺는 데(DNS·TCP·TLS) 쓴 시간입니다. 즉 캐시 여부가 만드는 차이는 0.1초 남짓이고, 나머지는 캐시로 줄일 수 없는 몫입니다. 캐시 규칙을 따로 걸어 즉시 반영을 포기할 이유가 크지 않다는 뜻입니다. 개인 블로그라면 기본값 그대로 두는 쪽을 권합니다.
함정 ③ www는 자동으로 붙지 않는다
aimakelog.app을 등록해도 www.aimakelog.app은 따로입니다. 확인해 보면 아예 연결이 안 됩니다 — 위 화면의 000이 그것으로, HTTP 상태 코드조차 못 받았다는 뜻입니다.
주소를 손으로 치는 사람이 www를 습관적으로 붙이는 경우가 있으니, 커스텀 도메인에 www도 추가하고 루트로 리다이렉트를 걸어두는 게 안전합니다. 이 글을 쓴 뒤 실제로 추가했고, 지금은 www로 들어와도 열립니다.
그런데 여기서 함정 안의 함정을 하나 더 만났습니다. 리다이렉트 규칙을 걸 때 경로를 보존하도록 설정하지 않으면 모든 주소가 홈으로 떨어집니다.
www.사이트/글-주소/ → 301 → https://사이트/ ← 글이 아니라 홈
누가 www 붙은 글 링크를 공유하면 받는 사람은 그 글이 아니라 홈페이지를 보게 됩니다. Cloudflare 리다이렉트 규칙에서 경로 접미사 보존(Preserve path suffix)을 켜면 해결됩니다. 규칙을 만든 뒤에는 루트뿐 아니라 글 주소로도 한 번 눌러보고 어디에 도착하는지 확인하세요.
함정 ④ pages.dev 주소도 같이 공개된다
프로젝트를 만들면 프로젝트명.pages.dev가 자동으로 생기고, 커스텀 도메인을 붙여도 그 주소는 계속 살아 있습니다. 같은 내용이 두 주소로 열린다는 뜻이라 중복 콘텐츠가 걱정됩니다.
다행히 정적 내보내기를 제대로 했다면 방어가 이미 돼 있습니다. pages.dev로 연 페이지의 canonical이 실제 도메인을 가리킵니다.
<link rel="canonical" href="https://aimakelog.app/simply-static-export/">
내보낼 때 주소를 전부 실제 도메인으로 치환했기 때문입니다(지난 편의 Force URL replacements). 사이트맵도 실제 도메인만 가리키고요. 그래도 신경 쓰인다면 대시보드에서 pages.dev 접근을 막을 수 있습니다.
기록해둔 함정이 사라져 있었다
이 글을 쓰면서 겪은 일을 하나 남겨둡니다. 이 블로그의 배포 메모에는 “wrangler가 자동화 환경에서 배포를 거부하니 가짜 터미널을 씌워야 한다”고 적혀 있었습니다. 실제로 그때는 그랬고, 그래서 명령 앞에 script -q /dev/null을 붙여 왔습니다.
그런데 글에 쓰려고 다시 재보니 재현되지 않았습니다. 터미널 없이, 가짜 터미널도 없이 그냥 돌려도 배포가 그대로 됩니다.
$ npx -y wrangler pages deploy build --project-name=aimakelog --branch=main < /dev/null
✨ Success! Uploaded 0 files (2416 already uploaded) (0.49 sec)
✨ Deployment complete!
도구가 그새 고쳐진 것인지, 애초에 원인이 다른 데 있었는지는 이제 알 수 없습니다. 확실한 건 메모에 적힌 함정이 지금은 함정이 아니라는 것뿐입니다.
남에게 전할 만한 교훈은 이겁니다. 도구는 계속 바뀌므로 겪었던 함정도 유통기한이 있습니다. 글에 넣기 전에 한 번 더 재보면, 없어진 문제를 붙들고 있느라 명령이 복잡해지는 걸 막을 수 있습니다.
전체 순서 정리
# ① 처음 한 번만 — 브라우저 로그인
npx -y wrangler login
# ② 배포 (지난 편의 내보내기·404·색인이 끝난 뒤)
npx -y wrangler pages deploy build --project-name=<프로젝트> --branch=main
# ③ 도메인은 대시보드에서 커스텀 도메인 추가 — DNS·인증서 자동
정리
- Cloudflare Pages 배포는 명령 한 줄 — 바뀐 파일만 올라가서 글 한 편 추가는 1~2초다
- 무료 한도는 파일 2만 개·개당 25MiB. 워드프레스는 코어 자산이 76%라 글이 늘어도 파일은 천천히 는다
- 월 500빌드 한도는 Git 연동 빌드 얘기다 — 폴더를 직접 올리는 방식에는 해당하지 않는다
- 루트 도메인이 A 레코드로 보이는 건 정상이다 (CNAME 플래트닝 + 프록시)
404.html이 없으면 없는 주소가 200을 돌려준다 — 색인이 오염되는 조용한 사고다- HTML은 엣지 캐시를 안 탄다. 대신 즉시 반영을 얻었고, 실제 속도 차이는 0.1초 남짓이었다
- www와 pages.dev 주소는 방치하면 각각 접속 실패와 중복 노출로 남는다
여기까지가 Cloudflare Pages 무료 배포의 전부입니다. 로컬에서 쓴 글이 인터넷에 올라가는 길이 완성됐습니다. 다음 편은 이 사이트로 애드센스를 신청하기까지 남은 일들입니다.