Cloudflare Pages: GitHub 푸시로 정적 사이트 배포하기

정적 사이트(static site)를 하나 만들고 나면 어디에 올릴지부터 고민하게 되는데요. GitHub Pages는 간편하지만 PR마다 변경 사항을 미리 열어 볼 주소를 띄우기 어렵고, 리디렉션이나 응답 헤더를 마음대로 바꿀 수도 없습니다. 🤔

Cloudflare Pages는 GitHub 저장소를 연결해 두면 푸시할 때마다 알아서 빌드하고 Cloudflare의 전 세계 네트워크에 배포해 주는 서비스입니다. 운영 브랜치가 아닌 브랜치에 푸시하면 따로 설정하지 않아도 프리뷰 배포(preview deployment)가 만들어지고, 무료 플랜에서도 대역폭 제한 없이 쓸 수 있습니다.

이번 글에서는 대시보드에서 GitHub 저장소를 연결해 첫 배포를 해 본 뒤, 프리뷰 배포와 브랜치 설정, Wrangler CLI로 직접 올리는 방법, GitHub Actions 배포, 커스텀 도메인 연결까지 차례로 살펴보겠습니다. 마지막에는 2026년 들어 Cloudflare가 새 프로젝트를 Workers로 안내하기 시작한 변화도 짚어 보겠습니다.

Cloudflare Pages의 두 가지 배포 방식

Pages 프로젝트에 사이트를 올리는 방법은 두 가지입니다.

하나는 Git 연동(Git integration)으로, GitHub나 GitLab 저장소를 연결해 두면 Cloudflare가 커밋마다 저장소를 받아 빌드 명령을 실행하고 결과물을 배포합니다. 다른 하나는 직접 업로드(Direct Upload)로, 로컬이나 CI에서 이미 빌드한 결과물 폴더를 Wrangler CLI나 대시보드의 드래그 앤 드롭으로 올립니다.

어느 방식이든 프로젝트를 만들면 <프로젝트 이름>.pages.dev 주소가 생기고, 배포할 때마다 <해시>.<프로젝트 이름>.pages.dev 형태의 고유 주소가 하나씩 붙습니다. 배포가 쌓여도 예전 주소는 그대로 살아 있어서, 특정 시점의 사이트가 궁금할 때 언제든 다시 열어 보면 됩니다.

처음 프로젝트를 만들 때 어느 방식으로 할지 신중히 골라야 하는데요, 직접 업로드로 만든 프로젝트는 나중에 Git 연동으로 바꿀 수 없기 때문입니다. 푸시만으로 배포되길 원한다면 처음부터 Git 연동으로 만들고, 빌드 과정을 GitHub Actions에서 직접 다루고 싶다면 직접 업로드로 만듭니다.

무료 플랜은 2026년 10월 기준으로 한 달에 빌드 500회, 동시 빌드 1개, 사이트당 파일 20,000개까지 쓸 수 있고, 빌드 하나는 20분이 지나면 중단됩니다. 개인 블로그나 문서 사이트라면 넉넉한 수준입니다.

대시보드에서 Pages 프로젝트 만들기

Cloudflare 대시보드의 Compute 메뉴에서 Workers & Pages로 들어가 Create application을 누르면 다음 화면이 나옵니다.

Workers & Pages의 Create an app 화면과 하단의 Continue to Pages 링크

여기서 Continue with GitHub를 누르면 Pages가 아니라 Workers 프로젝트가 만들어지니 주의하세요. Pages 프로젝트를 만들려면 화면 아래의 “Need to use the legacy Pages workflow?” 옆에 있는 Continue to Pages 링크를 눌러야 합니다. 대시보드가 Pages를 레거시(legacy) 흐름으로 부르는 이유는 글 마지막에서 다시 다루겠습니다.

Continue to Pages를 누르면 앞에서 설명한 두 방식 중 하나를 고르는 화면이 나옵니다.

Pages의 Get started 화면에서 고르는 Git 저장소 가져오기와 파일 직접 업로드

Import an existing Git repository가 Git 연동이고, Drag and drop your files가 직접 업로드입니다. 먼저 Git 연동부터 해 보겠습니다.

GitHub 저장소 연결하기

Import an existing Git repository에서 Get started를 누르면 GitHub 계정을 연결하는 단계가 나옵니다. 처음이라면 GitHub에 Cloudflare 앱을 설치하는 화면으로 넘어가는데요, 조직 저장소라면 조직 소유자이거나 GitHub 앱 관리 권한이 있어야 합니다. 앱이 접근할 저장소는 All repositories보다 배포할 저장소만 골라 두는 편이 안전합니다.

앱을 설치하고 돌아오면 연결한 GitHub 계정과 저장소 목록이 보입니다.

Cloudflare Pages에 연결할 GitHub 계정과 저장소를 고르는 화면

GitHub 계정 드롭다운에서는 개인 계정과 조직을 오가고, Add account로 다른 계정을 추가합니다. 찾는 저장소가 목록에 없다면 GitHub 쪽 앱 설정에서 그 저장소에 대한 접근을 허용했는지 확인하세요.

저장소를 고르고 Begin setup을 누르면 빌드 설정 화면이 나옵니다.

Astro 프레임워크 프리셋을 골라 빌드 명령과 출력 디렉토리가 채워진 빌드 설정 화면

Project name은 pages.dev 하위 도메인이 되므로 짧고 알아보기 쉬운 이름으로 정합니다. 이름이 이미 다른 사람에게 쓰이고 있으면 뒤에 무작위 문자가 붙습니다. Production branch에는 운영 배포를 담당할 브랜치를 고르고, 나머지 브랜치는 모두 프리뷰 배포 대상이 됩니다.

Build settings의 Framework preset은 자주 쓰는 프레임워크의 빌드 명령과 출력 디렉토리를 채워 주는 기능입니다. 위 화면처럼 Astro를 고르면 Build command에 npm run build, Build output directory에 dist가 들어가는데요, 프리셋 목록에는 Next.js(정적 내보내기), Nuxt, SvelteKit, Hugo, Jekyll, VitePress 등 30개 가까운 프레임워크가 있습니다. 빌드 이미지에는 Bun도 미리 설치되어 있어서 빌드 명령을 bun run build로 바꿔 써도 됩니다. 빌드가 필요 없는 순수 HTML 사이트라면 프리셋을 None으로 두고 빌드 명령을 비운 채 출력 디렉토리만 지정합니다.

Root directory (advanced)는 한 저장소에 여러 프로젝트가 있는 모노레포(monorepo)에서 사이트가 있는 하위 디렉토리를 가리킬 때 쓰고, Environment variables (advanced)에는 빌드할 때 읽을 환경 변수를 넣습니다. 여기 넣은 변수는 빌드 명령에만 전달되니, 브라우저에 노출되면 안 되는 값을 프레임워크의 공개 변수(PUBLIC_, VITE_ 접두사 등)로 넣지 않도록 주의하세요. 빌드 결과물에 값이 그대로 박힙니다.

Save and Deploy를 누르면 첫 빌드가 시작되고, 끝나면 <프로젝트 이름>.pages.dev에서 사이트가 열립니다. 이후로는 운영 브랜치에 푸시할 때마다 같은 과정이 자동으로 반복됩니다.

PR마다 프리뷰 배포 확인하기

Git 연동의 가장 큰 장점은 프리뷰 배포가 저절로 생긴다는 점입니다. 운영 브랜치가 아닌 브랜치에 푸시하면 Cloudflare가 그 커밋을 빌드해 운영과 분리된 프리뷰 환경에 배포하고, PR이 열려 있다면 봇이 PR에 댓글을 남깁니다.

PR에 Cloudflare Pages 봇이 남긴 프리뷰 URL 댓글

댓글에는 주소가 두 개 있는데요. Preview URL은 514c08dd.ajaedale.pages.dev처럼 커밋 하나에 고정된 주소라서, 같은 브랜치에 다시 푸시하면 새 주소가 생기고 이전 주소는 이전 커밋을 계속 보여 줍니다. 반면 Branch Preview URL은 pagination.ajaedale.pages.dev처럼 브랜치 이름으로 만든 브랜치 별칭(branch alias)이라서, 항상 그 브랜치의 최신 배포를 가리킵니다. 기획자나 디자이너에게 확인을 부탁할 때는 브랜치 별칭을 공유해 두면 푸시할 때마다 주소를 다시 알려 줄 필요가 없습니다.

프리뷰 배포가 검색 엔진에 잡혀 운영 사이트와 중복 콘텐츠가 될까 걱정할 수도 있는데요, 프리뷰 주소에 요청해 보면 Cloudflare가 응답에 noindex 헤더를 알아서 붙여 줍니다.

curl -sI https://feature-hello.pages-demo-87m.pages.dev/ | grep -i robots
결과
x-robots-tag: noindex

운영 주소에는 이 헤더가 붙지 않으니, 검색 노출은 robots.txt와 메타 태그로 평소처럼 관리하면 됩니다.

다만 프리뷰 배포는 기본적으로 누구나 열어 볼 수 있습니다. 공개 전 기능이 담긴 프리뷰를 팀 안에서만 보고 싶다면 Settings 탭 General 섹션의 Preview access에서 Restrict previews를 켜세요. Cloudflare Access가 프리뷰 주소 앞에 로그인 화면을 세워 주고, 운영 주소는 그대로 공개됩니다.

브랜치와 빌드 설정 바꾸기

프로젝트를 만든 뒤에 빌드 설정을 고치려면 Settings 탭의 Build 섹션으로 갑니다.

Git이 연결된 Pages 프로젝트의 Settings 탭 Build 섹션

Build configuration에서는 처음에 넣은 빌드 명령과 출력 디렉토리를 고치고, Build comments를 끄면 PR 댓글이 더 이상 달리지 않습니다. Build watch paths는 빌드 감시 경로(build watch paths)로, 기본값 *는 어떤 파일이 바뀌든 빌드합니다. 모노레포라면 apps/web/*처럼 사이트에 해당하는 경로만 남겨 두세요. README만 고쳐도 빌드가 돌면서 한 달 빌드 횟수를 금방 써 버리게 됩니다.

Branch control의 연필 아이콘을 누르면 오른쪽에 브랜치 제어(branch control) 패널이 열립니다.

운영 브랜치와 프리뷰 브랜치를 정하는 Branch control 패널

Production branch에서는 운영 브랜치를 바꾸고, Enable automatic production branch deployments를 끄면 운영 브랜치에 푸시해도 배포가 일어나지 않습니다. Preview branch에서는 프리뷰 배포 대상을 정하는데요, 기본값인 All non-Production branches는 모든 브랜치를 배포하고, None은 프리뷰 배포를 끄며, Custom branches를 고르면 나타나는 Include Preview branches와 Exclude Preview branches에 브랜치 이름 패턴을 적어 포함하거나 제외할 수 있습니다. Dependabot 같은 봇이 만드는 브랜치까지 매번 빌드하는 게 아깝다면 Exclude Preview branches에 dependabot/* 패턴을 넣어 두면 됩니다.

Settings 탭에서 Variables and secrets를 바꿨다면 다음 배포부터 반영된다는 점도 기억하세요. 값만 바꾸고 기다리면 사이트가 그대로라서 한참 헤매기 쉬운데요, Deployments 탭에서 최신 배포의 Retry deployment로 다시 배포해 주면 됩니다.

Wrangler로 직접 업로드하기

이번에는 Git 연동 없이 Wrangler로 빌드 결과물을 바로 올려 보겠습니다. 예제로 dist 폴더에 HTML 파일 하나만 있는 사이트를 준비했습니다.

dist/index.html
<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <title>Pages 데모</title>
  </head>
  <body>
    <h1>Cloudflare Pages에 오신 걸 환영합니다 👋</h1>
  </body>
</html>

먼저 프로젝트를 만듭니다. --production-branch로 지정한 브랜치로 배포하면 운영 환경에, 나머지 브랜치로 배포하면 프리뷰 환경에 올라갑니다.

bunx wrangler pages project create pages-demo --production-branch main
결과
 ⛅️ wrangler 4.151.1
────────────────────
✨ Successfully created the 'pages-demo' project. It will be available at https://pages-demo-87m.pages.dev/ once you create your first deployment.
To deploy a folder of assets, run 'wrangler pages deploy [directory]'.

pages-demo.pages.dev는 이미 누군가 쓰고 있어서 뒤에 -87m이 붙었네요. 이제 wrangler pages deploy에 결과물 폴더를 넘겨 배포합니다.

bunx wrangler pages deploy dist --project-name pages-demo --branch main
결과
 ⛅️ wrangler 4.151.1
────────────────────
Uploading... (0/1)
Uploading... (1/1)
✨ Success! Uploaded 1 files (1.42 sec)

🌎 Deploying...
✨ Deployment complete! Take a peek over at https://05cf5239.pages-demo-87m.pages.dev

Git 저장소 안에서 실행하면 Wrangler가 현재 브랜치를 읽어 --branch를 알아서 채워 주니, 평소에는 생략해도 됩니다. 기능 브랜치에서 배포하면 커밋 주소와 함께 브랜치 별칭이 생깁니다.

bunx wrangler pages deploy dist --project-name pages-demo --branch feature-hello
결과
 ⛅️ wrangler 4.151.1
────────────────────
Uploading... (0/1)
Uploading... (1/1)
✨ Success! Uploaded 1 files (0.77 sec)

🌎 Deploying...
✨ Deployment complete! Take a peek over at https://8155e6b6.pages-demo-87m.pages.dev
✨ Deployment alias URL: https://feature-hello.pages-demo-87m.pages.dev

대시보드의 Deployments 탭을 열어 보면 두 배포가 환경별로 나뉘어 있습니다.

Wrangler로 배포한 Pages 프로젝트의 운영 배포와 프리뷰 배포 목록

main에서 올린 배포는 Production, feature-hello에서 올린 배포는 Preview로 표시됩니다. 운영 배포를 이전 버전으로 되돌리고 싶다면 목록에서 해당 배포의 더보기 메뉴(…)에서 Rollback to this deployment를 고르면 다시 빌드하지 않고 바로 전환됩니다. 같은 목록을 터미널에서 보려면 다음 명령을 실행합니다.

bunx wrangler pages deployment list --project-name pages-demo

참고로 2026년 10월 기준으로 Claude Code 같은 AI 코딩 에이전트 안에서 새 프로젝트에 wrangler pages project create나 wrangler pages deploy를 실행하면 결과가 달라집니다. Wrangler가 에이전트를 감지하면 Pages 프로젝트 대신 같은 이름의 Worker를 만들어 정적 자산(static assets)으로 배포하고, wrangler.jsonc까지 생성해 버립니다. 이미 있는 Pages 프로젝트에 배포하거나 functions/ 폴더, _worker.js, _routes.json이 있는 프로젝트라면 이렇게 넘어가지 않고, 새 프로젝트라도 --force를 붙이면 Pages로 만들어집니다. 에이전트에게 Pages 배포를 맡겼는데 workers.dev 주소가 나왔다면 이 동작 때문입니다.

GitHub Actions로 배포하기

배포 전에 테스트나 린트를 반드시 통과시키고 싶다면 빌드를 GitHub Actions에 맡기고, 결과물만 직접 업로드로 올리는 구성을 씁니다. 앞에서처럼 wrangler pages project create로 직접 업로드 프로젝트를 먼저 만들어 두세요.

Wrangler가 GitHub 러너(runner)에서 Cloudflare에 로그인하려면 API 토큰이 필요합니다. 대시보드의 Manage account에 있는 Account API tokens에서 Create Token을 누르고, Custom token으로 만들면서 권한을 Account, Cloudflare Pages, Edit로 지정합니다. 만든 토큰과 계정 ID를 저장소 시크릿으로 등록합니다.

gh secret set CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID

명령을 실행하면 값을 입력하라는 프롬프트가 떠서 셸 기록에 토큰이 남지 않습니다. 저장소, 환경, 조직 시크릿의 차이는 GitHub Actions 시크릿과 변수 관리하기에서 자세히 다뤘습니다.

이제 워크플로우 파일을 만듭니다.

.github/workflows/deploy.yml
name: Deploy

on:
  push:

permissions:
  contents: read
  deployments: write

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: oven-sh/setup-bun@v2

      - run: bun install --frozen-lockfile
      - run: bun test
      - run: bun run build

      - name: Deploy
        uses: cloudflare/wrangler-action@v4
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist --project-name pages-demo --branch ${{ github.head_ref || github.ref_name }}
          gitHubToken: ${{ secrets.GITHUB_TOKEN }}

push 이벤트에 브랜치를 걸지 않았기 때문에 어느 브랜치에 푸시하든 배포가 실행되고, --branch 값에 따라 main은 운영으로, 나머지는 프리뷰로 갑니다. gitHubToken을 넘기면 wrangler-action이 GitHub 배포(deployment) 기록을 남겨 커밋과 PR에서 배포 주소로 바로 들어갈 수 있게 해 주는데, 이를 위해 deployments: write 권한이 필요합니다. 권한 설정은 GitHub Actions의 권한 설정을 참고하세요.

Git 연동과 비교하면 PR 댓글이 자동으로 달리지 않고 워크플로우 파일도 직접 관리해야 하지만, bun test가 실패하면 배포 단계가 실행되지 않으니 깨진 코드가 운영에 나갈 일이 없습니다. 같은 흐름을 Workers로 구성하는 방법은 Cloudflare Workers 자동 배포에서 다뤘으니 함께 보시면 좋습니다.

_redirects와 _headers로 응답 바꾸기

Pages는 결과물 폴더 최상위에 특별한 이름의 파일 두 개를 두면 리디렉션과 응답 헤더를 바꿔 줍니다. 서버 코드 없이 파일만으로 설정하니, 정적 사이트 생성기의 public/ 폴더에 넣어 두면 빌드할 때 결과물에 함께 복사됩니다.

_redirects에는 한 줄에 하나씩 원래 경로, 이동할 주소, 상태 코드를 적습니다.

dist/_redirects
/blog/*  https://www.daleseo.com/:splat  301
/docs  /  302

*로 받은 나머지 경로는 :splat으로 이어 붙일 수 있습니다. 상태 코드를 생략하면 302가 쓰이는데, 주소를 영구히 옮겼다면 301을 적어야 검색 엔진이 새 주소로 신호를 넘겨줍니다. 상태 코드의 차이는 HTTP 상태 코드에서 정리했습니다. 플레이스홀더, 규칙 순서, 끝 슬래시 처리 같은 세부 문법과 다른 플랫폼과의 차이는 _redirects 글에서 따로 다뤘습니다.

_headers에는 경로 패턴을 적고 그 아래에 들여쓰기로 붙일 헤더를 나열합니다.

dist/_headers
/*
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin

배포한 뒤 요청해 보면 두 파일이 그대로 적용돼 있습니다.

curl -sI https://pages-demo-87m.pages.dev/blog/python-zip | grep -iE "^HTTP|^location"
curl -sI https://pages-demo-87m.pages.dev/ | grep -iE "^HTTP|x-frame|referrer"
결과
HTTP/2 301
location: https://www.daleseo.com/python-zip
HTTP/2 200
referrer-policy: strict-origin-when-cross-origin
x-frame-options: DENY

URL 끝 슬래시는 따로 설정하지 않아도 빌드 결과물의 파일 구조를 따라 정리됩니다. 자세한 동작은 Trailing slash 글에서 다뤘습니다.

커스텀 도메인 연결하기

pages.dev 대신 내 도메인으로 서비스하려면 프로젝트의 Custom domains 탭에서 Set up a custom domain을 누르고 도메인을 입력합니다. 도메인이 이미 Cloudflare DNS에서 관리되고 있다면 Cloudflare가 추가할 DNS 레코드를 보여 줍니다.

커스텀 도메인 연결 시 Cloudflare가 추가할 CNAME 레코드 확인 화면

demo.daleseo.com을 입력하니 demo라는 이름으로 pages-demo-87m.pages.dev를 가리키는 CNAME 레코드를 만들겠다고 하네요. Activate domain을 누르면 레코드가 추가되고, 인증서 발급이 끝나는 몇 분 뒤부터 HTTPS로 접속됩니다.

도메인을 다른 DNS 업체에서 관리하고 있다면 그 업체의 관리 화면에서 같은 CNAME 레코드를 직접 추가하면 됩니다. 다만 이 방법은 www나 blog 같은 하위 도메인에만 쓸 수 있고, example.com 같은 루트 도메인(apex domain)을 연결하려면 네임서버를 Cloudflare로 옮겨야 합니다. CNAME 레코드가 무엇인지 낯설다면 DNS 레코드 글을, GitHub Pages에서 같은 작업을 하는 방법은 GitHub Pages 커스텀 도메인 글을 참고하세요.

새 프로젝트라면 Workers도 고려하기

앞에서 대시보드가 Pages를 레거시 흐름으로 부른다고 했는데요. Pages 공식 문서 첫 화면에도 “Start new projects with Workers.”라는 안내가 붙어 있습니다. Cloudflare가 Workers를 주력 플랫폼으로 삼고, 정적 사이트도 Workers의 정적 자산 기능으로 올리도록 권하고 있는 거죠. Pages가 당장 없어지는 건 아니라서 이미 운영 중인 프로젝트는 그대로 둬도 되지만, 새로 시작한다면 차이를 알고 고르는 게 좋습니다.

항목Cloudflare PagesWorkers 정적 자산
설정대시보드 빌드 설정wrangler.jsonc의 assets
기본 주소<프로젝트>.pages.dev<Worker>.<계정>.workers.dev
프리뷰 배포브랜치마다 자동previews 설정과 프리뷰 빌드를 켜야 함
브랜치 제어대시보드에서 포함, 제외 패턴 지정운영 브랜치와 비운영 브랜치 빌드 여부 정도
외부 DNS 도메인하위 도메인 CNAME으로 연결 가능네임서버가 Cloudflare에 있어야 함
서버 코드functions/ 폴더의 Pages 함수Worker 코드 전체, 크론과 큐 등 모든 기능
_redirects 등결과물 폴더 최상위정적 자산 디렉토리 안

정리하면 Pages는 Git만 연결하면 프리뷰까지 알아서 해 주는 간편함이, Workers는 크론 트리거나 큐, 점진적 배포 같은 기능을 모두 쓸 수 있는 확장성이 강점입니다. 순수 정적 사이트에 PR 프리뷰만 있으면 되는 경우나 도메인을 다른 DNS 업체에 두고 싶은 경우라면 지금도 Pages가 편하고, 나중에 서버 기능이 붙을 사이트라면 처음부터 Workers로 시작하는 편이 옮기는 수고를 덜어 줍니다. 기존 Pages 프로젝트를 옮기고 싶다면 Cloudflare의 Pages에서 Workers로 이전하기 문서에 기능별 대응표가 정리되어 있습니다.

마치며

지금까지 Cloudflare Pages로 정적 사이트를 배포하는 방법을 살펴봤습니다. 대시보드에서 GitHub 저장소를 연결하면 푸시만으로 운영 배포와 PR 프리뷰가 만들어지고, Wrangler로 결과물을 직접 올리거나 GitHub Actions에서 테스트를 통과한 뒤에만 배포하도록 구성할 수도 있습니다. _redirects와 _headers 파일로 응답을 다듬고 커스텀 도메인까지 연결하면 웬만한 정적 사이트 호스팅은 Pages 하나로 충분합니다. 🚀

다만 처음 프로젝트를 만들 때 Git 연동과 직접 업로드 중 무엇을 고를지, 그리고 Pages와 Workers 중 어디서 시작할지는 나중에 바꾸기 어려우니 한 번 더 따져 보고 정하세요.

더 자세한 내용은 Cloudflare Pages 공식 문서를 참고하세요.

This work is licensed under CC BY 4.0CCBY

개발자를 위한 뉴스레터

달레가 정리한 AI 개발 트렌드와 직접 만든 콘텐츠를 전해드립니다.

Discord