CloudFront에 올린 Font(woff, woff2)가 CORS 때문에 차단되는 경우
폰트가 계속 차단이 되는 경우 확인해 볼 것
CORS 에러가 발생한 상황
프로젝트를 하다가 유독 Web Font(woff, woff2)만 CORS에 의해 막히는 이상한 현상이 발생했습니다.
아래와 같이 CSS에서 우리가 CDN에 올려준 Font를 불러오도록 했습니다.
Access-Control-Allow-Origin 응답 Header에 이 Font를 요청하는 사이트 주소도 잘 추가되어 있었습니다.
일단 저는 S3에 폰트를 업로드하고 CDN이랑 연동을 했습니다. 그리고 CDN에서는 S3 bucket의 CORS 정책을 쓰도록 했습니다.
CloudFront와 CORS 정책은 아무 문제가 없어 보였습니다.
그런데 계속 Access-Control-Allow-Origin에 의해 CORS 의해 막혔습니다. Console에 뜬 메시지는 다음과 같습니다.
Access to font at 'https://cdn.주소.com/font.woff2' from origin 'https://www.주소.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
정말 이상했던게 다른 AJAX 요청은 잘 되는데 유독 Font 파일만 가져올 때 CORS Error가 나는 것이 이상했습니다.
처음에는 이전에 CORS 정책을 안 넣어줬었는데 그것 때문인가 했습니다. 그래서 cache invalidation을 하고 다른 잘되는 request의 Last-Modified 값을 봤는데 전부 최신 파일을 받아오고 괜찮아 보였습니다.
그래서 팀원이 검색을 해서 다음과 같은 글을 발견했습니다.
https://stackoverflow.com/questions/52893199/cors-issue-with-woff2-fonts-behind-cdn-in-chrome
Content-Type을 application/font-woff2으로 해줘야지 Invalidation이 제대로 된다 이런 말이 있더군요. (하지만 저건 옛날 MIME입니다. 최신으로 찾아보시고 설정하세용.) 하지만 이것도 해결이 안 됐습니다.
문제
답은 의외로 간단한 곳에 있었습니다.
크롬이 친절히 다 알려줬습니다.
"님 Preflight Request Block 됨"
Preflight Request가 차단돼서 발생한 오류였던 것입니다.
Webfont는 Preflight Request를 보낸다
제가 예전에 쓴 CORS 글에서도 간단히 다룬 적이 있습니다.
Simple Request의 조건은 간단하게 다음과 같습니다. (더 정확한 것은 문서를 확인해주세용.)
- HTTP Method가 GET, POST, HEAD 중 하나
- Content-Type이 아래 중 하나이다
- text/plain
- application/x-www-form-urlencoded
- multipart/form-data
WebFont는 font/woff, WebFont2는 font/woff2라는 MIME type을 쓰고 있습니다. 모두 Simple Request의 Content-Type 조건은 아닙니다.
따라서 WebFont의 Content-Type은 Preflight처리됩니다.
제가 오류가 발생한 이유는 Preflight Request용도로 사용된는 OPTIONS 메서드를 허용하지 않아서 허용하는게 무엇인지 브라우저가 알 수 없으니 CORS 정책에 의해 차단되고 있었던 겁니다.
Preflight Request란?
Simple Request가 아닌 Request는 Preflight Reqeust를 먼저 보내서 몇 가지를 확인을 받도록 되어있습니다.
Preflight Request는 먼저 브라우저가 OPTIONS 메서드로 원하는 리소스에 대해 HTTP 요청을 보내도 괜찮은지 확인하는 것입니다.
예를 들어서, 제가 어떤 폰트에 GET 요청을 보냈다고 해봅시다.
// AJAX Request from Browser
fetch('https://cdn.주소.com/awesome-font.woff2')
그럼 브라우저는 자동으로 Simple Request에 해당하지 않으므로 아래와 같이 Preflight Request를 만들어서 보냅니다.
OPTIONS /awesome-font HTTP/1.1
Origin: https://www.웹사이트주소.com
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Content-Type, ...생략
...생략
그럼 미리 서버가 Origin이랑 Header를 보고 아래와 같이 허용하는 것 응답으로 알려줍니다.
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.웹사이트주소.com
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: Content-Type, ...생략
...생략
브라우저는 "음..! 서버에서는 이런 것들을 허용하고 있군!" 한번 확인을 하고 제대로 Request를 보냅니다.
GET /awesome-font HTTP/1.1
Origin: https://www.웹사이트주소.com
Content-Type: font/woff2
...생략
Preflight에서 허용하는 것에 맞추어 본래 Request가 보내지면 서버에서는 font파일을 보내주게 됩니다.
해결법
해결법은 Preflight Request를 허용해주면 됩니다.
위에서 설명했다시피 브라우저는 Simple Request가 아닌 경우에 실제 Request를 보내기 전 Preflight Request를 보냅니다. 이것을 위해 HTTP OPTIONS 요청을 사용합니다.
OPTIONS /나의소중한webfont HTTP/1.1
Origin: https://cdn.주소.com
Access-Control-Request-Method: GET
저는 바보같이 GET 요청만 할테니깐 GET, HEAD만 허용했었습니다. OPTIONS도 허용해줍시다.
CloudFront에서 미리 요청을 보낼 수 있도록 OPTIONS 메서드 허용해줍시다.
CloudFront > Distribution 선택 > Behaviours > Edit
S3 CORS 정책은 건드릴 필요 없습니다. (애초에 CORS AllowedMethods로 "OPTIONS"를 지원하지도 않습니다.)
그러며 해결될 겁니다!
참고한 문서
- https://developer.mozilla.org/ko/docs/Web/HTTP/CORS#%EB%8B%A8%EC%88%9C_%EC%9A%94%EC%B2%ADsimple_requests
- https://coding-groot.tistory.com/91#simple-request%EC%9D%B8-%EA%B2%BD%EC%9A%B0
- https://stackoverflow.com/questions/52893199/cors-issue-with-woff2-fonts-behind-cdn-in-chrome
- https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/ManageCorsUsing.html#cors-allowed-methods
댓글
이 글 공유하기
다른 글
-
HTTP 1.1 - 같은 서버(도메인)에 관한 동시 연결 제한
HTTP 1.1 - 같은 서버(도메인)에 관한 동시 연결 제한
2024.09.12 -
CertBot 인증서가 만료가 되었다
CertBot 인증서가 만료가 되었다
2024.01.21 -
웹 문서를 만들기 전에 고민해볼 것들
웹 문서를 만들기 전에 고민해볼 것들
2022.04.17 -
Flask를 CLI에서 실행해야 하는 이유와 환경 세팅하기
Flask를 CLI에서 실행해야 하는 이유와 환경 세팅하기
2021.06.08