GitHub PR의 체크 목록에서 Workers Builds: <레포 이름> 만 빨갛게 실패하는데 사이트는 Cloudflare Pages로 매번 정상 배포된다면, 원인은 하나입니다.
같은 레포가 Workers 와 Pages 에 둘 다 연결된 것입니다. 실패 로그의 마지막 줄은 이렇습니다.
wrangler versions upload
✘ Missing entry-point to Worker script or to assets directory
레포에 wrangler.jsonc도 wrangler.toml도 없으니 당연한 결과입니다.
wrangler versions upload은 Worker 스크립트의 엔트리 포인트나 정적 자산 디렉터리 경로를 받는 명령입니다. 잘못 붙은 Workers 연결이 자기 일을 하려다 그 인자가 없어 실패한 것일 뿐입니다.
고치는 방법은 Workers 프로젝트의 설정 → 빌드에서 저장소 연결을 해제하는 것입니다.
프로젝트 삭제까지는 필요 없습니다. 연결만 끊으면 새 커밋에 체크가 안 붙습니다.
⚠️ 다만 Cloudflare 목록에는 같은 이름의 프로젝트가 두 줄 보입니다.
이름 대신 URL(/workers/services/view/… 대 /pages/view/…)이나 체크의 details_url 로 어느 쪽인지 판별한 뒤에 끊어야 합니다.
이름만 보고 지우면 사이트가 내려갑니다.
아래는 왜 같은 이름이 둘 생기는지, 설정 파일을 만들어 초록으로 만들면 안 되는 이유, 지우기 전에 봐야 하는 화면 두 장, 그리고 저 빨간 X를 4일 동안 “원래 저런 것”으로 두었던 비용입니다.
환경
- Astro 7 정적 사이트, 커스텀 도메인 1개, 의존성 3개
- 배포: Cloudflare Pages (Git 연동)
- CI: GitHub Actions
- 레포에
wrangler.jsonc·wrangler.toml없음 (전체 이력에서 추가된 적 없음) - 확인에 쓴 도구:
ghCLI (GitHub check-runs API)
왜 같은 이름이 둘 생길까?
체크가 왜 둘인가
체크 목록에는 Cloudflare 항목이 둘 있습니다.
두 체크는 같은 GitHub App 이 답니다 (cloudflare-workers-and-pages).
앱이 하나라서 “Cloudflare 연결”도 하나라고 읽힙니다.
그런데 서로 다른 제품의 서로 다른 프로젝트가 같은 앱을 통해 각자 체크를 답니다.
$ gh api repos/OWNER/REPO/commits/$SHA/check-runs \
--jq '.check_runs[] | [.name, .conclusion, .app.slug] | @tsv'
Workers Builds: anchorforest-blog failure cloudflare-workers-and-pages
Cloudflare Pages success cloudflare-workers-and-pages
check success github-actions
이름이 왜 겹치는가
대시보드를 열면 더 선명합니다.
“Workers 및 Pages” 목록에 anchorforest-blog 이라는 항목이 두 줄 나옵니다.
Cloudflare 는 레포 이름에서 프로젝트 이름을 따옵니다.
같은 레포를 두 번 연결하면 이름이 충돌하지 않고 그냥 같은 이름이 둘 생깁니다.
구분되는 건 URL 하나뿐입니다.
각 항목을 클릭했을 때 주소창이 dash.cloudflare.com/<account-id>/workers/services/view/anchorforest-blog/… 과 …/pages/view/anchorforest-blog/… 으로 갈립니다.
체크에서 바로 확인하고 싶으면 details_url 을 보면 됩니다. 대시보드를 열 필요도 없어요.
초록으로 만들면 왜 안 되나?
설정 파일을 만들면 초록이 됩니다
그리고 하면 안 됩니다.
wrangler.jsonc 를 하나 만들면 빨간 X 가 초록으로 바뀝니다.
그러면 실패하던 연결이 성공하기 시작하고, 같은 커밋이 두 곳에 배포됩니다. 빨간 X가 사라진 대가로 배포 경로가 둘이 됩니다.
실패하는 체크가 하는 일이 애초에 필요 없는 일이면 고칠 게 아니라 없앨 것입니다.
대시보드에서 처음 눌러본 “Workers Logs” 비활성화도 답이 아니었습니다. 그건 로그 수집 기능이고 체크를 만드는 건 빌드입니다.
연결을 끊은 바로 다음 PR에서 체크가 셋에서 둘로 줄었고, 사이트는 HTTP 200이었습니다.
지우기 전에 볼 화면 두 장
제가 세운 근거는 부정 명제였습니다
지우기 전에 저는 이렇게 적었습니다.
“빌드가 한 번도 성공한 적 없다 = 업로드된 Worker 버전이 없다 = 그게 사이트를 서빙할 수 없다”
2주 뒤 이 글을 쓰려고 당시 체크 기록을 API로 전부 뽑아 보니 첫 줄이 거짓이었습니다.
PR 브랜치 커밋은 31전 31패였지만 main 커밋 28개는 전부 성공했고 Version ID까지 발급돼 있었습니다.
저는 PR 화면만 봤고 main 화면은 한 번도 열지 않았습니다. 왜 브랜치에서는 실패하고 main에서는 성공했는지는 지금 확인할 수 없습니다. 확인에 필요한 빌드 설정과 로그는 저장소 연결을 끊을 때 같이 사라졌습니다(집계와 추측은 부록에).
결론은 맞았습니다. 지워도 안전했고 사이트는 멀쩡합니다.
다만 이유가 제가 적어둔 것과 다릅니다.
제가 확인한 것은 anchorforest.com 이 Pages 프로젝트의 사용자 지정 도메인으로 등록돼 있다는 것과, 연결을 끊은 뒤에도 그 도메인이 anchorforest-blog.pages.dev 와 같은 HTML 을 내려준다는 것까지입니다. Worker가 도메인에 붙는 자리는 커스텀 도메인 말고도 Route가 있습니다. anchorforest.com/* 같은 패턴을 Worker에 걸어두면 그 Worker가 요청을 먼저 받을 수 있습니다. 그럼 Pages와 Worker 라우트가 같은 도메인에 함께 있을 때 어느 쪽이 먼저일까요? 그 우선순위를 명시한 공식 문서를 못 찾았고 저도 재보지 않았으니, 이건 가능성이고 확인된 동작이 아닙니다. 그렇게 걸린 Worker가 요청을 손대지 않고 흘려보내면 HTML은 똑같이 나오니, 응답이 같다는 확인은 Worker가 없다는 증명이 되지 못합니다. 그래서 지우기 전에 볼 화면은 두 장입니다.
| 확인할 것 | 어디서 | 제가 봤나 |
|---|---|---|
| 커스텀 도메인이 Pages 프로젝트에 등록돼 있다 | Pages → 사용자 지정 도메인 | ✅ |
| Worker에 그 도메인을 향한 Route가 없다 | Workers → 설정 → Triggers → Routes | ❌ |
| Worker에 Custom Domain이나 다른 호출 바인딩이 없다 | Workers → 설정 → Triggers → Custom Domains | ❌ |
“없어도 되는 것”을 증명하는 대신 “필요한 것이 어디 붙어 있는지”를 보는 쪽이 짧습니다.
다만 긍정 명제도 붙을 수 있는 자리를 전부 덮어야 성립합니다.
저는 자리 하나만 보고 끝냈습니다.
그래서 이 글의 결론도 “안전했다”까지는 못 가고 “안전했던 것으로 보이지만 확인은 덜 했다”입니다.
방치하면 무엇을 잃나?
상시 빨강의 대가
“원래 저런 것”으로 4일을 지나갔습니다.
같은 기간에 붙여 둔 주 1회 감시 잡이 도입 이래 한 번도 성공한 적이 없었습니다. 종료코드 127이었고 8일 동안 아무도 몰랐습니다.
그 8일이 여기와 겹칩니다.
상시 빨강은 새 빨강을 흡수합니다. 체크 목록에 늘 빨간 게 하나 있으면 두 개가 됐을 때 눈이 그걸 세지 않습니다.
“어차피 실패해도 되는 체크”에도 비용이 있습니다.
자주 묻는 질문
프로젝트를 삭제해야 하나요
연결만 끊으면 됩니다. 새 커밋에 체크가 안 붙어요. 삭제는 되돌리기 어렵고, 어느 쪽이 사이트를 서빙하는지 확실하지 않은 상태에서 지우면 사이트가 내려갑니다.
어느 쪽이 Pages 인지 어떻게 구별하나요
이름으로는 구별이 안 됩니다. URL 이 갈립니다 — /workers/services/view/… 와 /pages/view/… 입니다. 대시보드를 안 열고 체크의 details_url 만 봐도 됩니다.
왜 브랜치에서는 실패하고 main 에서는 성공했나요
모릅니다. 확인에 필요한 빌드 설정과 로그가 저장소 연결을 끊을 때 같이 사라졌습니다. 집계와 추측은 부록에 있습니다.
연결을 끊으면 사이트가 내려가지 않는다고 어떻게 확신했나요
확신하지 못했습니다.
제가 확인한 것은 anchorforest.com 이 Pages 프로젝트의 사용자 지정 도메인으로 등록돼 있다는 것과, 연결을 끊은 뒤에도 같은 HTML 이 나온다는 것까지입니다.
Worker Route 가 같은 도메인에 걸려 있을 가능성은 재보지 않았어요.
참고 자료
- Cloudflare —
wrangler versions upload은 그 명령이 엔트리 포인트나 정적 자산 디렉터리 경로를 받는다는 근거입니다. 실패 로그의 마지막 줄이 그 인자를 못 찾았다는 뜻인 이유가 여기 있습니다.
남는 것
빨간 X를 “원래 저런 것”으로 분류하는 순간 그 목록은 감시 기능을 잃습니다. 상시 실패 하나가 새 실패 하나를 가립니다. 저는 그 상태에서 8일짜리 고장을 놓쳤습니다. 정상이라고 판단했으면 지우고, 못 지우겠으면 왜 못 지우는지를 적어둡니다.
같은 이름의 프로젝트가 둘일 수 있습니다. Cloudflare는 레포 이름에서 프로젝트 이름을 따옵니다. 목록에서 이름으로 고르면 사이트를 서빙하는 쪽을 지울 수 있습니다. 판별은 URL(/workers/services/view/ vs /pages/view/)이나 체크의 details_url로 합니다.
빨간 걸 초록으로 만드는 것이 목표가 아닙니다. 설정 파일 하나면 저 체크는 초록이 됩니다. 그리고 같은 커밋이 두 곳에 배포되기 시작합니다.
“없어도 되는 이유”보다 “필요한 것이 어디 붙어 있는지”를 확인하십시오. 제가 세운 안전 근거는 부정 명제(“성공한 적이 없다”)였고, 그래서 제가 안 본 화면 하나로 무너졌습니다. 긍정 명제로 바꾸면 확인이 짧아지지만, 붙을 수 있는 자리를 전부 세어야 합니다. Cloudflare에서 도메인 앞에 코드가 끼어드는 자리는 Pages의 사용자 지정 도메인에 더해 Worker의 Routes와 Custom Domains까지입니다.
지우기 전에 뽑아둘 것을 뽑아두십시오. 저장소 연결을 끊으면 빌드 로그와 설정도 같이 안 보이게 됩니다. 이 글의 일부는 그때 남겨둔 게 없어서 추측으로 남았습니다.
부록 1: 성공과 실패 집계
| 커밋 종류 | Workers Builds 성공 | 실패 |
|---|---|---|
| PR 브랜치 커밋 | 0 | 31 |
| main 커밋(머지 커밋 27 + 직접 커밋 1) | 28 | 0 |
성공한 체크의 응답에는 Build ID와 Script, Version ID가 들어 있습니다.
Build ID: d297557e-…
Script: anchorforest-blog
Version ID: 86f70a81-…
로그에 찍힌 명령이 wrangler versions upload였으니 비프로덕션 브랜치와 프로덕션 브랜치가 서로 다른 명령을 쓰는 쪽이 그럴듯하지만, 이건 추측입니다. 응답이 같다는 확인은 이렇게 했습니다.
$ curl -sS https://anchorforest.com/ > a.html
$ curl -sS https://anchorforest-blog.pages.dev/ > b.html
$ cmp a.html b.html && echo 동일
동일
부록 2: 재현
체크가 어느 앱에서 오는지, 두 Cloudflare 체크가 같은 앱 슬러그를 갖습니다.
gh api repos/OWNER/REPO/commits/$SHA/check-runs \
--jq '.check_runs[] | [.name, .conclusion, .app.slug] | @tsv'
브랜치 커밋과 main 커밋의 판정이 갈리는지, 제가 뒤늦게 돌린 게 이겁니다. 부모가 둘인 커밋이 머지 커밋입니다.
for sha in $(git log --all --format=%H --since=2026-08-20 --until=2026-08-28); do
r=$(gh api repos/OWNER/REPO/commits/$sha/check-runs \
--jq '.check_runs[] | select(.name|startswith("Workers Builds")) | .conclusion' 2>/dev/null)
[ -z "$r" ] && continue
p=$(git log -1 --format=%p $sha | wc -w | tr -d ' ')
for c in $r; do echo "parents=$p $c"; done
done | sort | uniq -c
체크를 지우지 않고 어느 쪽 프로젝트인지 판별하려면 details_url의 경로만 보면 됩니다.
gh api repos/OWNER/REPO/commits/$SHA/check-runs \
--jq '.check_runs[] | "\(.name)\t\(.details_url)"'
이 글에서 고친 것 1개
- 2026-09-27 —
wrangler versions upload가 받는 인자에 문서 링크를 달았습니다. 그리고 “Worker 라우트가 Pages 앞에서 먼저 실행된다”를 “먼저 받을 수 있다”로 고쳤습니다. 그 우선순위를 명시한 공식 문서를 못 찾았고 재보지도 않았는데 확인된 동작처럼 적었습니다. 지우기 전에 볼 화면이 두 장이라는 결론은 그대로입니다 — 확인하지 않은 자리가 있다는 것이 근거이기 때문입니다.
이 사이트의 수치는 주 1회 DB와 다시 대조하고, 판단이 바뀌면 지우지 않고 글 안에 덧붙입니다. 갱신은 RSS로 받을 수 있습니다.