Devliner

Cloudflare R2 업로드는 되는데 브라우저에서 막힐 때 CORS 점검 순서

Cloudflare R2 업로드는 되는데 브라우저에서 막힐 때 CORS 점검 순서 대표 이미지

Cloudflare R2 업로드는 되는데 브라우저에서 막힐 때 CORS 점검이 필요한 이유

S3 API로 업로드는 성공하는데 브라우저에서 이미지나 파일을 불러올 때 CORS 에러가 발생한다면, 업로드 클라이언트는 서버 환경에서 실행되어 브라우저 보안 정책의 영향을 받지 않지만 브라우저에서 직접 R2 객체에 접근할 때는 CORS 정책을 따라야 하기 때문입니다. R2 버킷에 CORS 규칙이 없거나 잘못 설정되면 브라우저는 응답을 차단합니다.

Cloudflare R2 CORS 점검 핵심 항목

1. R2 버킷 CORS 규칙 존재 여부

  • 확인 대상: Cloudflare 대시보드에서 해당 R2 버킷의 CORS 설정이 등록되어 있는지 확인합니다.
  • 확인 방법: Cloudflare 대시보드에 로그인한 뒤 R2 메뉴에서 버킷을 선택하고 Settings 탭의 CORS Policy 섹션을 엽니다. 빈 상태이거나 "No CORS policy configured" 메시지가 표시되는지 확인합니다.
  • 실패 신호: CORS Policy 섹션이 비어 있거나 규칙이 하나도 등록되지 않은 경우 브라우저는 모든 cross-origin 요청을 차단합니다.
  • 조치: Cloudflare 문서에서 제공하는 JSON 형식으로 CORS 규칙을 작성해 버킷에 추가합니다. 최소한 AllowedOrigins, AllowedMethods, AllowedHeaders를 포함해야 합니다.

2. AllowedOrigins 값과 요청 도메인 일치

  • 확인 대상: CORS 규칙의 AllowedOrigins 배열에 브라우저가 요청을 보내는 도메인이 정확히 포함되어 있는지 확인합니다.
  • 확인 방법: 브라우저 개발자 도구 Network 탭에서 실패한 요청의 Headers를 열고 Origin 헤더 값을 복사한 뒤, R2 버킷 CORS 규칙의 AllowedOrigins와 대조합니다. 프로토콜(http/https), 포트 번호까지 정확히 일치해야 합니다.
  • 실패 신호: Origin: https://example.com인데 CORS 규칙에 http://example.com이나 example.com만 등록되어 있으면 일치하지 않습니다. 콘솔에 "Access to fetch at ... has been blocked by CORS policy" 에러가 표시됩니다.
  • 조치: AllowedOrigins에 정확한 프로토콜과 도메인을 추가하거나, 모든 도메인을 허용하려면 ["*"]를 사용합니다. 단 와일드카드는 인증 정보를 포함한 요청과 함께 사용할 수 없습니다.

3. AllowedMethods에 실제 HTTP 메서드 포함

  • 확인 대상: 브라우저가 사용하는 HTTP 메서드(GET, HEAD, PUT 등)가 CORS 규칙의 AllowedMethods 배열에 명시되어 있는지 확인합니다.
  • 확인 방법: 개발자 도구 Network 탭에서 차단된 요청의 Method를 확인한 뒤, R2 버킷 CORS 규칙의 AllowedMethods 배열과 비교합니다. 이미지 로드는 GET, 업로드는 PUT 또는 POST를 사용합니다.
  • 실패 신호: 브라우저가 GET 요청을 보냈는데 AllowedMethods에 GET이 없으면 preflight OPTIONS 요청이 실패하거나 본 요청이 차단됩니다.
  • 조치: AllowedMethods 배열에 필요한 메서드를 추가합니다. 읽기 전용이라면 ["GET", "HEAD"], 업로드까지 허용하려면 ["GET", "HEAD", "PUT", "POST", "DELETE"]를 포함합니다.

4. AllowedHeaders와 요청 헤더 일치

  • 확인 대상: 브라우저가 전송하는 커스텀 헤더나 인증 헤더가 CORS 규칙의 AllowedHeaders에 포함되어 있는지 확인합니다.
  • 확인 방법: Network 탭에서 preflight OPTIONS 요청의 Access-Control-Request-Headers 헤더 값을 확인한 뒤, CORS 규칙의 AllowedHeaders와 대조합니다. 대소문자는 구분하지 않지만 헤더 이름이 정확히 일치해야 합니다.
  • 실패 신호: 요청이 Authorization 헤더를 포함하는데 AllowedHeadersAuthorization이 없으면 preflight가 실패하고 본 요청이 전송되지 않습니다.
  • 조치: AllowedHeaders에 필요한 헤더를 추가하거나, 모든 헤더를 허용하려면 ["*"]를 사용합니다. 일반적으로 ["Content-Type", "Authorization", "Range"]를 포함합니다.

5. ExposeHeaders 설정 (응답 헤더 접근 필요 시)

  • 확인 대상: 브라우저 JavaScript에서 응답 헤더를 읽어야 한다면 해당 헤더가 ExposeHeaders에 명시되어 있는지 확인합니다.
  • 확인 방법: 코드에서 response.headers.get('ETag') 같은 호출을 사용하는지 확인한 뒤, CORS 규칙의 ExposeHeaders 배열에 해당 헤더가 있는지 점검합니다.
  • 실패 신호: ETagContent-Length 같은 헤더를 읽으려 하는데 ExposeHeaders에 없으면 JavaScript에서 null을 반환합니다.
  • 조치: ExposeHeaders에 필요한 헤더를 추가합니다. 예: ["ETag", "Content-Length", "Content-Range"]. 기본적으로 노출되는 헤더는 추가하지 않아도 됩니다.

6. MaxAgeSeconds 캐시 기간

  • 확인 대상: preflight 응답을 브라우저가 캐시하는 시간이 적절하게 설정되어 있는지 확인합니다.
  • 확인 방법: CORS 규칙의 MaxAgeSeconds 값을 확인합니다. 값이 너무 짧으면 매 요청마다 preflight가 발생하고, 너무 길면 CORS 규칙 변경이 즉시 반영되지 않습니다.
  • 실패 신호: CORS 규칙을 수정했는데 브라우저가 계속 이전 규칙을 따르는 것처럼 동작하면 캐시 기간이 아직 남아 있을 수 있습니다.
  • 조치: 개발 중에는 MaxAgeSeconds600(10분) 정도로 설정하고, 운영 환경에서는 86400(24시간)으로 늘려 preflight 요청을 줄입니다.

점검 순서

  1. 버킷 CORS 규칙 존재 확인: 대시보드에서 규칙이 등록되어 있는지 먼저 확인합니다.
  2. 브라우저 에러 메시지 수집: 개발자 도구 Console과 Network 탭에서 CORS 관련 에러와 요청 헤더를 기록합니다.
  3. Origin 일치 검증: 요청의 Origin 헤더와 CORS 규칙의 AllowedOrigins를 대조합니다.
  4. Method 일치 검증: 요청 Method와 AllowedMethods를 비교합니다.
  5. Headers 일치 검증: preflight의 Access-Control-Request-HeadersAllowedHeaders를 대조합니다.
  6. 규칙 적용 및 테스트: CORS 규칙을 수정한 뒤 브라우저 캐시를 비우고 다시 요청합니다.
  7. 응답 헤더 확인: Network 탭에서 응답에 Access-Control-Allow-Origin 헤더가 포함되어 있는지 최종 점검합니다.

자주 놓치는 포인트

  • 프로토콜과 포트 불일치: http://localhost:3000https://localhost:3000은 다른 origin입니다. 개발 환경에서 프로토콜을 바꿨다면 CORS 규칙도 함께 수정해야 합니다.
  • 와일드카드 서브도메인: AllowedOrigins*.example.com 형식은 지원되지 않습니다. 각 서브도메인을 개별적으로 등록하거나 ["*"]를 사용해야 합니다.
  • preflight 요청 실패: 브라우저가 본 요청 전에 OPTIONS 요청을 보내는데, 이 단계에서 실패하면 본 요청이 전송되지 않습니다. Network 탭에서 OPTIONS 요청의 상태 코드와 응답 헤더를 확인합니다.
  • CDN 캐시: R2 앞에 Cloudflare CDN이나 다른 프록시가 있다면 CORS 헤더가 캐시될 수 있습니다. 규칙을 수정한 뒤 캐시를 purge해야 변경 사항이 즉시 반영됩니다.
  • 인증 정보 포함 요청: credentials: 'include' 옵션을 사용하는 fetch 요청은 AllowedOrigins에 와일드카드를 사용할 수 없고 정확한 도메인을 명시해야 합니다.

재사용 체크리스트

  1. R2 대시보드 → 버킷 → Settings → CORS Policy 섹션 확인
  2. 브라우저 개발자 도구 → Network 탭 → 실패 요청 선택 → Headers 탭 열기
  3. Origin 헤더 값 복사 → CORS 규칙 AllowedOrigins와 비교 (프로토콜, 포트 포함)
  4. 요청 Method 확인 → CORS 규칙 AllowedMethods와 비교
  5. preflight OPTIONS 요청 → Access-Control-Request-Headers 확인 → CORS 규칙 AllowedHeaders와 비교
  6. 응답 헤더 접근 필요 시 → CORS 규칙 ExposeHeaders에 해당 헤더 추가
  7. CORS 규칙 수정 → 브라우저 캐시 비우기 → 요청 재시도
  8. 응답 Headers 탭 → Access-Control-Allow-Origin 헤더 존재 및 값 확인
  9. 문제 지속 시 → CDN 캐시 purge → 규칙 JSON 문법 오류 점검

참고 자료

  • Cloudflare Docs - Configure CORS https://developers.cloudflare.com/r2/buckets/cors/
  • Cloudflare Docs - Upload objects https://developers.cloudflare.com/r2/objects/upload-objects/
  • Cloudflare
  • R2
  • 업로드는

관련 글

전체 보기