Serving Pages
Cloudflare Pages가 정적 에셋을 서빙하고 라우팅을 처리하는 방식을 설명합니다.
라우팅 규칙
기본 라우팅
Pages는 요청된 경로와 일치하는 HTML 파일을 자동으로 서빙합니다:
| 요청 경로 | 서빙되는 파일 |
|---|---|
/ | /index.html |
/about | /about.html 또는 /about/index.html |
/blog/post | /blog/post.html 또는 /blog/post/index.html |
Clean URLs
Pages는 자동으로 Clean URL을 적용합니다:
/contact.html → 리다이렉트 → /contact/about.html → 리다이렉트 → /about/blog/post.html → 리다이렉트 → /blog/postHTML 확장자가 있는 URL로 접근하면 확장자가 없는 URL로 자동 리다이렉트됩니다.
디렉토리 인덱스
디렉토리의 index.html은 해당 디렉토리 경로로 서빙됩니다:
/blog/index.html → /blog/ 또는 /blog/docs/guide/index.html → /docs/guide/ 또는 /docs/guide404 에러 처리
기본 404 페이지
프로젝트 루트에 404.html 파일을 생성하면 커스텀 404 페이지로 사용됩니다:
dist/├── index.html├── about.html└── 404.html ← 커스텀 404 페이지계층적 404 페이지
Pages는 디렉토리 구조를 따라 가장 가까운 404 페이지를 찾습니다:
dist/├── index.html├── 404.html ← 기본 404 페이지├── blog/│ ├── index.html│ └── 404.html ← /blog/* 요청용 404 페이지└── docs/ ├── index.html └── 404.html ← /docs/* 요청용 404 페이지404 페이지 매칭 순서:
/blog/unknown요청 시 →/blog/404.html확인- 없으면 →
/404.html확인 - 없으면 → 기본 Pages 404 페이지
섹션별 404 예시
<!DOCTYPE html><html><head> <title>블로그 글을 찾을 수 없습니다</title></head><body> <h1>블로그 글을 찾을 수 없습니다</h1> <p>요청하신 블로그 글이 존재하지 않습니다.</p> <a href="/blog">블로그 목록으로 돌아가기</a></body></html>Single-Page Application (SPA) 모드
SPA 모드 활성화
루트 레벨에 404.html이 없으면, Pages는 SPA 프레임워크(React, Vue, Angular 등)로 가정하고 SPA 모드를 활성화합니다.
SPA 모드에서는:
- 모든 경로가 루트(
/)의index.html로 매핑됩니다 - 클라이언트 사이드 라우터가 URL을 처리합니다
/about,/help,/users/123등의 경로가 모두index.html을 반환합니다
SPA 프로젝트 구조
dist/├── index.html ← 모든 경로에서 이 파일 서빙├── assets/│ ├── app.js│ └── style.css└── (404.html 없음) ← SPA 모드 활성화SPA 모드 비활성화
SPA 모드를 비활성화하려면 루트에 404.html을 추가합니다:
dist/├── index.html├── 404.html ← SPA 모드 비활성화└── assets/SPA vs 정적 사이트
| 기능 | SPA 모드 | 정적 사이트 모드 |
|---|---|---|
| 404.html | 없음 | 있음 |
| 알 수 없는 경로 | index.html 반환 | 404.html 반환 |
| 라우팅 | 클라이언트 사이드 | 서버 사이드 |
| SEO | 추가 설정 필요 | 기본 지원 |
캐싱
자동 캐싱
Pages는 최적의 신선도를 위해 자동 캐싱을 적용합니다:
- 에셋은 다음 배포까지 캐시됩니다
- 새 배포 시 캐시가 자동으로 무효화됩니다
- 브라우저 캐싱은
ETag헤더와304 Not Modified응답을 사용합니다
캐시 동작
첫 번째 요청:GET /assets/app.jsResponse: 200 OKETag: "abc123"
두 번째 요청 (캐시 검증):GET /assets/app.jsIf-None-Match: "abc123"Response: 304 Not Modified (본문 없음)커스텀 도메인에서의 캐싱
주의: 커스텀 도메인에서 별도의 캐싱 설정을 추가하지 마세요.
커스텀 캐싱이 문제를 일으킬 수 있는 경우:
- 배포 후에도 오래된 콘텐츠가 제공됨
- 리다이렉트가 제대로 작동하지 않음
- Pages Functions 응답이 캐시됨
Pages의 기본 캐싱 전략을 그대로 사용하는 것을 권장합니다.
기본 HTTP 헤더
Pages는 모든 응답에 다음 헤더를 자동으로 포함합니다:
보안 헤더
X-Content-Type-Options: nosniffMIME 타입 스니핑을 방지합니다.
CORS 헤더
Access-Control-Allow-Origin: *모든 출처에서의 리소스 접근을 허용합니다.
캐시 헤더
Cache-Control: public, max-age=0, must-revalidateETag: "hash-value"콘텐츠 타입
Content-Type: text/html; charset=utf-8Content-Type: application/javascriptContent-Type: text/css파일 확장자에 따라 적절한 MIME 타입이 설정됩니다.
에셋 서빙
정적 에셋
다음 파일들은 자동으로 적절한 Content-Type과 함께 서빙됩니다:
| 확장자 | Content-Type |
|---|---|
.html | text/html |
.css | text/css |
.js | application/javascript |
.json | application/json |
.png | image/png |
.jpg, .jpeg | image/jpeg |
.svg | image/svg+xml |
.woff2 | font/woff2 |
.pdf | application/pdf |
압축
Pages는 자동으로 응답을 압축합니다:
- Gzip: 대부분의 클라이언트 지원
- Brotli: 최신 브라우저에서 더 나은 압축률
클라이언트의 Accept-Encoding 헤더에 따라 적절한 압축 방식이 선택됩니다.
실전 구성 예시
Astro 정적 사이트
dist/├── index.html├── about/│ └── index.html├── blog/│ ├── index.html│ ├── post-1/│ │ └── index.html│ └── post-2/│ └── index.html├── 404.html└── _astro/ ├── app.abc123.js └── style.def456.cssReact SPA
dist/├── index.html ← 모든 라우트의 진입점├── static/│ ├── js/│ │ └── main.js│ └── css/│ └── main.css└── assets/ └── images/Next.js 정적 내보내기
out/├── index.html├── about.html├── blog/│ ├── index.html│ └── [slug].html├── 404.html├── _next/│ └── static/└── images/문제 해결
404가 예상대로 동작하지 않음
- SPA 모드 확인: 루트에
404.html이 있는지 확인 - 파일 위치 확인: 빌드 출력에 404.html이 포함되었는지 확인
- 계층 구조 확인: 섹션별 404 페이지의 경로가 올바른지 확인
Clean URL이 작동하지 않음
- 파일 구조 확인:
.html파일이 올바른 위치에 있는지 확인 - 리다이렉트 충돌:
_redirects파일의 규칙이 충돌하지 않는지 확인
캐시 문제
- 브라우저 캐시 삭제: 개발 중에는 하드 리프레시 (Ctrl+Shift+R)
- 배포 확인: 새 배포가 완료되었는지 확인
- 커스텀 캐시 규칙 제거: 커스텀 도메인의 추가 캐시 설정 제거
SPA 라우팅 문제
- 404.html 제거: SPA 모드를 위해 루트 404.html 삭제
- base URL 확인: 프레임워크의 base URL 설정 확인
- 정적 에셋 경로: 에셋이 절대 경로를 사용하는지 확인