S3 CORS — 와일드카드 한 단계와 presigned POST

클라우드·인프라17분조회 –

사용자가 올리는 파일을 서버가 받아서 다시 S3 로 넘기지 않고 브라우저가 S3 에 바로 올리게 해둔 기능이 있었습니다. 서버는 업로드를 허가하는 presigned 요청만 만들어 주고, 실제 바이트는 브라우저 → S3 로 직접 가는 구조라, 서버 대역폭을 안 쓰고 큰 파일도 그대로 올라갑니다.

다만 브라우저가 S3 라는 다른 출처로 직접 요청을 보내는 구조라, S3 버킷 쪽 CORS 규칙에 그 출처가 허용돼 있어야 합니다. 한 번 걸어 두면 그 뒤로는 볼 일이 없는 설정이라 올려 두고 잊고 있었고, 그러다 그 업로드가 막혔습니다.

Access to fetch at '...s3...' from origin 'https://a.studio.example.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header

버킷에 CORS 규칙은 이미 걸려 있었으니, 그 규칙이 지금 요청과 실제로 매칭되는지를 한 줄씩 맞춰 봤습니다.

와일드카드는 한 단계 라벨만 매칭

기존 CORS 규칙의 AllowedOrigins 는 이랬습니다.

"AllowedOrigins": ["https://*.example.com"]

https://*.example.com 은 https://foo.example.com 은 매칭하지만 https://a.studio.example.com 은 매칭 안 합니다. S3 CORS 의 * 는 점 하나 사이의 한 단계 라벨만 대체해서, 두 단계(a.studio)를 한 번에 한 규칙으로 같이 처리하지 못합니다.

요청 origin 이 a.studio.example.com 이라 두 단계였고, 어떤 규칙에도 안 걸리니 Access-Control-Allow-Origin 헤더가 안 붙었습니다.

foo.example.com라벨 한 단계 → 매칭a.studio.example.com라벨 두 단계 → 매칭 안 됨Allow-Origin 붙음헤더 없음 → CORS 차단규칙: https://*.example.com
별표 하나는 라벨 한 단계까지만

PUT만 허용한 규칙에 안 걸리는 presigned POST

origin 을 고쳐도 여전히 막혔고, 기존 규칙의 AllowedMethods 는 GET, HEAD, PUT 인데 이 기능은 presigned POST 로 올리고 있었습니다.

presigned URL 업로드에는 PUT 방식과 폼 필드로 올리는 POST 방식이 있는데, 브라우저 멀티파트 업로드에서는 후자가 흔합니다. 둘은 서로 다른 HTTP 메서드라, PUT 만 허용한 CORS 규칙에는 POST 업로드가 안 걸립니다.

해결 — 별도 규칙으로 origin과 POST를 명시

두 단계 서브도메인용 규칙을 따로 만들고 POST 를 넣었습니다.

{
  "AllowedOrigins": ["https://*.studio.example.com"],
  "AllowedMethods": ["POST", "PUT", "GET", "HEAD"],
  "AllowedHeaders": ["*"]
}

*.studio.example.com 처럼 매칭시키고 싶은 단계에 정확히 와일드카드를 두고, 실제로 쓰는 메서드(여기선 POST)를 명시합니다.

CORS가 막혔을 때 볼 두 곳 — 라벨 매칭과 메서드

브라우저 에러 메시지는 Access-Control-Allow-Origin 헤더가 없다는 것까지만 말하고 왜 안 붙였는지는 안 알려주니, 규칙 매칭을 손으로 따져 보는 수밖에 없습니다.

볼 곳은 두 군데입니다. 먼저 요청 origin 이 와일드카드 규칙에 진짜 매칭되는지 봅니다. S3 의 * 는 한 라벨만 대체하니, 다단계 서브도메인이면 그 단계에 맞춰 규칙을 따로 둬야 합니다. 그다음은 요청 메서드가 AllowedMethods 에 있는지고, presigned POST 업로드는 PUT 규칙과 별개라 POST 를 안 넣으면 서명이 아무리 맞아도 CORS 에서 걸립니다.

  1. 불러오는 중