IT 시행착오··약 6분

Cloudflare Workers에서 404가 ‘빈 페이지’가 아닌 이유 — not_found_handling 실측

wrangler.jsonc의 not_found_handling: 404-page가 Astro 404.astro를 어떻게 연결하는지, dist/404.html과 robots noindex까지 레포 빌드로 확인한 메모.

404 오류 페이지 예시 — Workers not_found_handling과 Astro 커스텀 404 연결 (cc by-sa 3.0, wikimedia-commons)

존재하지 않는 URL을 치면 브라우저에 “Not Found” 한 줄만 뜨는 정적 호스팅을 써 본 적이 있다. seominhyuk.com은 Astro 정적 빌드 산출물을 Cloudflare Workers assets로 올린다. wrangler.jsoncnot_found_handling: "404-page"가 들어 있어, 없는 경로 요청이 커스텀 404 HTML로 떨어진다. 처음엔 “Astro가 404.astro를 만들면 알아서 되겠지”라고 넘겼는데, Workers 쪽 설정 키 이름이 헷갈려서 한번 직접 빌드 산출물을 열어봤다.

사실: 설정 한 줄과 dist/404.html

현재 wrangler.jsonc는 다음과 같다.

"assets": {
  "directory": "./dist",
  "html_handling": "drop-trailing-slash",
  "not_found_handling": "404-page"
}

npm run builddist/404.html이 생성된다(열람: 2026-08-13). 파일 크기는 약 14KB이고, <title>은 “페이지를 찾을 수 없습니다 | 서민혁닷컴”, <meta name="robots" content="noindex,follow">가 들어 있다. src/pages/404.astro에서 BaseLayoutrobots="noindex,follow"를 넘긴 결과가 그대로 정적 HTML에 박혔다. 즉 검색엔진에 404 URL을 색인시키지 않겠다는 의도가 빌드 단계에서 확정된다.

html_handling: drop-trailing-slash와 함께 보면, trailing slash 없는 URL이 이 사이트의 기본 형태다. /about은 200, /about/는 리다이렉트 또는 404 중 하나로 정리되는데, 이 조합은 workers-trailing-slash 글에서 다룬 것과 맞물린다. 404 처리는 그 다음 층위다. “경로 정규화가 끝난 뒤에도 없는 글”을 어떻게 보여줄지.

이유: Workers assets는 ‘파일 매칭 실패’를 따로 다룬다

S3나 단순 정적 호스팅은 객체가 없으면 XML 오류나 빈 404를 돌려주기 쉽다. Workers Static Assets는 not_found_handling으로 그 동작을 바꾼다. "404-page"미매칭 요청에 대해 404.html을 본문으로 내려주고 HTTP 상태도 404로 맞추는 모드다(Cloudflare 문서 기준). "single-page-application"과 혼동하면 안 된다. SPA 모드는 없는 경로도 index.html을 200으로 주는 패턴이고, 블로그처럼 글마다 고유 URL이 있는 SSG와는 목적이 다르다.

Astro output: 'static'에서는 src/pages/404.astro가 빌드 시 404.html로 나온다. Workers가 그 파일을 “not found 시 응답”으로 쓰려면 배포 산출물에 404.html이 실제로 있어야 한다. 로컬 astro preview만 확인하고 wrangler deploy를 빼먹으면, 프로덕션은 예전 Worker 버전의 404 동작을 유지한다. workers-builds-zero-manual-wrangler-loop에서 적었듯, git push만으로는 이 레포가 자동 갱신되지 않는다.

평가: 어디에 쓰고, 어디에 안 쓰는지

쓰는 경우: 정적 블로그·문서 사이트처럼 실제로 없는 slug는 404가 맞는 구조. 홈·카테고리 링크를 404 본문에 넣어 이탈을 줄이는 것도 404.astro의 역할이다. 현재 페이지는 카테고리 목록과 홈 링크를 나열한다.

안 쓰는 경우: 클라이언트 라우터만 있는 SPA 전체를 Workers assets에 올릴 때 "404-page"를 기본값처럼 두는 것. 그때는 의도적으로 "single-page-application"을 검토해야 한다. 또 404 본문에 민감한 디버그 정보(내부 경로, 스택)를 넣는 것도 금지다. 방문자에게는 짧은 안내만.

반례: 일부 CDN은 404 HTML을 200으로 내려주는 “소프트 404” 설정이 있다. Search Console에서 색인 문제가 나면 상태 코드와 본문을 curl로 함께 확인해야 한다. 이 레포는 noindex 메타까지 넣었으므로, 실수로 200이 나가도 색인 쪽 리스크는 한 겹 줄어 있다. 다만 상태 코드 404가 맞는지는 여전히 점검 대상이다.

부연: 배포 체크 한 줄

배포 후에는 존재하지 않는 slug 하나를 치고, (1) HTTP 404인지, (2) 본문에 사이트 헤더·카테고리 링크가 있는지, (3) robotsnoindex인지 보면 된다. 로컬에서는 dist/404.html 존재 여부만으로 1차 검증이 가능하다. Pagefind 인덱스에는 404가 들어가지 않는다(빌드 로그 기준 238페이지 인덱스, 404 제외).

내부 실측으로 남길 만한 수치: 2026-08-13 빌드에서 포스트 포함 정적 페이지 238개, dist/404.html 14,186바이트, not_found_handling404-page. Worker 이름 warpsense-blog, assets 디렉터리 ./dist.

출처

  • 내부 실측: wrangler.jsonc (not_found_handling, html_handling), src/pages/404.astro, npm run build 산출물 dist/404.html 메타·크기 (열람: 2026-08-13)
  • Cloudflare Workers Static Assets — Routingnot_found_handling 동작 설명 (열람: 2026-08-13)