개발자를 괴롭히는 붉은 콘솔 에러

정적 에셋을 새로운 CDN으로 방금 마이그레이션했습니다. 사이트 로딩 속도는 엄청나게 빨라졌지만, 브라우저 탭에는 기본 회색 지구본 모양만 보입니다. 개발자 도구 콘솔을 열어보니 붉은색 에러가 가득합니다: Access to fetch at 'https://cdn.yoursite.com/site.webmanifest' from origin 'https://yoursite.com' has been blocked by CORS policy.

파비콘 CORS(교차 출처 리소스 공유) 에러라는 까다롭고 짜증나는 세계에 오신 것을 환영합니다. 웹 매니페스트, SVG 아이콘 또는 동적으로 생성된 파비콘을 다른 서브도메인이나 타사 CDN에서 호스팅하는 경우, 최신 브라우저는 보안상의 이유로 이를 기본적으로 차단합니다.

실제 개발 업무로 돌아갈 수 있도록 이 문제를 지금 바로 해결해 보겠습니다.

빠른 해결책: HTML 속성 추가

시간이 없다면, 문제는 HTML <link> 태그에 속성이 누락되었을 가능성이 높습니다. 브라우저는 일반 이미지와 Fetch 요청을 다르게 처리합니다. <img> 태그는 일반적으로 엄격한 CORS 검사를 우회하지만, JSON 매니페스트나 동적 조작을 위한 SVG를 가져오는 것은 그렇지 않습니다.

이 문제를 즉시 해결하려면 매니페스트와 아이콘 링크에 crossorigin 속성을 추가하세요.

웹 매니페스트 링크 업데이트

<link rel='manifest' href='https://cdn.yoursite.com/site.webmanifest' crossorigin='use-credentials'>

왜 use-credentials를 사용할까요? 웹 앱 매니페스트를 로드하는 데 쿠키나 권한 부여가 필요한 경우(기업 내부 도구에서 흔함) 이 정확한 값이 필요합니다. CDN이 완전히 공개되어 있다면 대신 crossorigin='anonymous'를 사용할 수 있습니다.

SVG 아이콘 링크 업데이트

<link rel='icon' type='image/svg+xml' href='https://cdn.yoursite.com/icon.svg' crossorigin='anonymous'>

JavaScript를 통해 아이콘을 주입하거나 캔버스 요소에서 사용하는 경우, 이 anonymous 플래그는 브라우저에 사용자 자격 증명을 보내지 않고 파일을 요청하도록 지시하며, 이는 일반적으로 브라우저의 보안 모델을 충족합니다.

근본 원인: 브라우저가 파비콘을 차단하는 이유

단순한 16x16 픽셀 이미지가 왜 보안 잠금을 유발하는지 궁금할 수 있습니다. 이는 두 가지 특정 최신 웹 기능 때문입니다.

1. Fetch API 및 웹 매니페스트

site.webmanifest 파일은 이미지가 아니라 JSON 파일입니다. 브라우저가 rel='manifest' 태그를 보면 내부적으로 Fetch API를 사용하여 다운로드합니다. 모든 교차 출처 Fetch 요청은 엄격한 CORS 정책의 적용을 받습니다. HTML이 www.example.com에 있고 매니페스트가 assets.example.com에 있는 경우, 서버가 명시적으로 허용하지 않는 한 브라우저는 이를 차단합니다.

2. 오염된 캔버스(Tainted Canvas)

동적 파비콘(예: JavaScript를 사용하여 아이콘에 빨간색 알림 점 추가)을 구축하는 경우, 아이콘을 <canvas> 요소에 그리고 수정한 다음 데이터 URL로 내보낼 가능성이 높습니다. 원본 이미지가 CORS 헤더 없이 CDN에서 로드된 경우, 브라우저는 캔버스를 '오염된(tainted)' 것으로 표시합니다. 캔버스가 오염되면 toDataURL()을 호출할 때 치명적인 보안 에러가 발생합니다.

CORS 문제 이전에 기본적인 표시 문제로 어려움을 겪고 있다면 파비콘이 표시되지 않는 문제 해결 가이드를 확인해 보세요.

서버 측 해결책: CDN 구성

HTML을 수정하는 것은 절반의 성공에 불과합니다. CDN이나 스토리지 버킷(예: AWS S3 또는 Cloudflare)이 올바른 헤더를 반환하지 않으면 브라우저는 여전히 파일을 거부합니다.

에셋 서버가 Access-Control-Allow-Origin 헤더를 반환하도록 구성해야 합니다. 일반적인 설정 방법은 다음과 같습니다.

Nginx 구성

정적 에셋을 위해 자체 Nginx 서버를 관리하는 경우, 매니페스트 및 이미지에 대한 교차 출처 요청을 허용하도록 구성에 이 블록을 추가하세요:

location ~* \.(webmanifest|svg|png|ico)$ {
    add_header Access-Control-Allow-Origin "*";
    add_header Access-Control-Allow-Methods "GET, OPTIONS";
}

AWS S3 CORS 구성

S3 버킷에서 직접 에셋을 제공하는 경우, 다음 JSON 스니펫을 사용하여 AWS 콘솔을 통해 버킷의 CORS 정책을 업데이트해야 합니다:

[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "HEAD"],
        "AllowedOrigins": ["https://www.yoursite.com"],
        "ExposeHeaders": []
    }
]

실제 사례: GitHub의 처리 방식

GitHub를 살펴보겠습니다. 그들은 사용자 아바타와 UI 아이콘을 avatars.githubusercontent.com에서 호스팅합니다. 네트워크 요청을 검사해 보면 이러한 모든 에셋에 대해 명시적으로 Access-Control-Allow-Origin: *을 전송하는 것을 볼 수 있습니다.

왜 그럴까요? GitHub는 동적 UI 업데이트에 크게 의존하기 때문입니다. CORS 헤더를 완벽하게 구성하지 않으면 해당 이미지에서 픽셀 데이터를 읽으려는 클라이언트 측 스크립트가 브라우저 탭의 JavaScript 실행을 중단시킵니다. 그들은 이 서버 헤더를 HTML의 crossorigin='anonymous' 속성과 결합하여 완벽한 설정을 만듭니다.

예방 팁 및 개인적인 의견

수년간 이러한 문제를 디버깅해 온 결과, 저는 확고한 의견을 가지고 있습니다: 기본 favicon.ico는 루트 도메인에 유지하세요.

웹 매니페스트 및 고해상도 SVG와 같은 최신 에셋의 경우, 위와 같이 헤더를 구성하는 한 CDN에서 호스팅해도 괜찮습니다.

결론

파비콘 CORS 교차 출처 문제는 골칫거리지만, 브라우저의 보안 모델을 이해하면 완전히 예측 가능합니다. 링크 태그에 crossorigin 속성을 추가하고, CDN이 Access-Control-Allow-Origin 헤더를 전송하는지 확인하고, 루트 도메인에 폴백 아이콘을 유지하세요.

소규모 프로젝트에서 CDN 문제를 완전히 피하고 싶다면 Mzu favicondl을 사용하여 완벽하게 최적화된 로컬 아이콘 세트를 생성하세요. 때로는 교차 출처 문제를 해결하는 가장 좋은 방법은 동일한 출처에 머무르는 것입니다.