혼합 콘텐츠는 HTTPS 페이지의 HTTP 요청을 브라우저가 막는 줄입니다. 콘솔의 http 주소를 https로 바꾸고, 그림 업그레이드와 스크립트 차단을 나눕니다. 프리뷰에서만 카카오 스크립트가 죽는 자리와 CORS를 가릅니다. IP 호스트 이미지는 올라가지 않습니다. 보안, 버셀, 브라우저. 2026년 8월 MDN 기준.
혼합 콘텐츠는 HTTPS 페이지가 HTTP 주소를 불러 브라우저가 막거나, 그림과 영상과 소리만 HTTPS로 올려 치는 상황입니다. 콘솔의 Mixed Content 한 줄에서 http:// 주소를 찾아 HTTPS로 바꾸세요.
로컬 개발 서버는 http라 조용한데, 버셀 프리뷰에서만 로그인 스크립트가 실패하면 이 칸인 경우가 많습니다. 카카오 스크립트를 http로 두면 배포 페이지에서만 거절됩니다.
CORS는 다른 출처의 응답 헤더 문제이고, 혼합 콘텐츠는 페이지가 https인데 자원만 http인 짝입니다. MDN은 2026-08-15 문서에서 스크립트는 막고, 그림은 도메인이면 HTTPS로 올린다고 적습니다.
자물쇠 페이지가 http를 거절한다
페이지가 HTTPS이면, 그 안의 HTTP 요청은 이미 의심 대상입니다.
자물쇠는 그 문서가 변조되지 않았다는 표시인데, 문서 안 스크립트나 XHR이 평문 HTTP로 나가면 중간에서 바꿀 수 있습니다. 브라우저는 그래서 혼합을 줄입니다. MDN은 사이트가 혼합 콘텐츠와 혼합 다운로드를 피해야 한다고 적고, 브라우저는 그림, 영상, 소리의 HTTP를 HTTPS로 자동 업그레이드하고 그 밖의 자원은 막으며, 혼합 다운로드도 기본으로 막아야 한다고 설명합니다.
콘솔 문장에는 페이지 URL과 막힌 자원 URL이 같이 나옵니다. 자원 쪽이 http://이면 이번 칸입니다. 네트워크 탭의 스킴 열만 내려도 같은 목록이 나옵니다. 서버 로그에 요청이 없으면, 브라우저가 보내기 전에 자른 것입니다.
페이지 자물쇠가 HTTPS인데 자원만 HTTP이면 브라우저가 그 요청을 자른다
올려 주는 자원과 막는 자원
그림이라고 전부 올라가는 것은 아닙니다. IP 호스트는 막힙니다.
자원
요즘 브라우저
예
그림, 영상, 소리 (도메인)
HTTP를 HTTPS로 올려 요청
img src가 http인 CDN 도메인
같은 미디어인데 호스트가 IP
업그레이드하지 않고 차단
http://203.0.113.10/a.png
스크립트, 스타일, fetch, XHR, 폰트, 워커
차단
카카오 스크립트, API fetch
파일 다운로드
혼합 다운로드는 기본 차단 대상
http로 붙은 첨부
예전 스펙은 능동 혼합(스크립트, 스타일)을 반드시 막고, 표시용 혼합은 선택으로 뒀습니다. 2026년 MDN 본문은 그 둘을 업그레이드 대상과 차단 대상으로 다시 적습니다. 사파리는 혼합을 넓게 거절하는 쪽으로 알려져 있어, 크롬에서 그림이 떠도 사파리 검수는 따로 해야 합니다. HTTPS로 올린 주소에 파일이 없으면 HTTP로 되돌아가지 않고 그 요청은 실패합니다.
프리뷰에서 로컬 API를 부르는 자리
https 프리뷰가 http://localhost를 부르면 배포에서만 막힙니다.
개발 서버 페이지가 http://localhost:3000이면 그 문서 자체는 보안 문맥이 아니라서 혼합 규칙이 느슨합니다. 같은 코드를 버셀 프리뷰의 https:// 도메인에 올리면, http://localhost:4000이나 http://로 박힌 API는 혼합이 됩니다. 환경 변수에 스킴까지 넣고, 프리뷰 값만 https 공개 주소로 바꿉니다. 로컬용 http 기본값을 프로덕션 번들에 남기지 마세요.
한국 서비스에서 자주 남는 하드코딩은 카카오 SDK, 지도 타일, 예전에 http로 받던 이미지 호스트입니다. 페이지는 200인데 버튼만 죽은 화면이면 네트워크에서 막힌 스크립트 이름을 먼저 보세요. 인증서 오류(UNABLE_TO_VERIFY_LEAF_SIGNATURE)는 서버가 HTTPS로 나갔는데 노드가 인증서를 못 믿는 줄이라, 브라우저가 http를 자른 이번 칸과 다릅니다.
스킴을 환경에 두고 http를 남기지 않기
// 프리뷰와 프로덕션 값은 https로 시작해야 한다
const api = process.env.NEXT_PUBLIC_API_BASE;
// 나쁜 예: https 페이지에서 http 절대 경로
// fetch('http://api.example.com/orders')
fetch(api + '/orders');
upgrade-insecure-requests의 한계
이 지시문은 주소를 올려 치울 뿐, 없는 HTTPS를 만들어 주지 않습니다.
MDN의 Content-Security-Policy: upgrade-insecure-requests는 사이트의 안전하지 않은 URL을 HTTPS로 바꿔 요청하라고 브라우저에 알립니다. 메타 태그로도 같은 내용을 넣을 수 있습니다. 탐색이 아닌 자원 요청은 네트워크에 나가기 전에 스킴이 바뀝니다. 대상 호스트가 443을 안 열면 실패만 남고 HTTP 폴백은 없습니다.
이 지시문은 다른 사이트에 걸린 링크로 들어온 최상위 이동까지 HTTPS로 보장하지 않습니다. MDN은 그래서 HSTS를 대신할 수 없고, max-age가 있는 Strict-Transport-Security를 따로 두라고 합니다. 예전 block-all-mixed-content에만 기대던 설정도 지금 문서의 업그레이드와 차단 구분으로 다시 읽어야 합니다. 소스의 http를 지우는 작업이 먼저이고, CSP는 남은 주소를 올리는 보조입니다.
보안 패널이나 콘솔의 혼합 경고를 위에서부터 적습니다. 각 줄의 자원 URL을 소스에서 찾습니다. 내 코드의 문자열이면 https로 바꿉니다. HTML을 내려 주는 CMS나 마크다운에 http://가 남아 있으면 저장 값을 고칩니다. 상대 경로(/api/orders)는 페이지 스킴을 따라가므로 혼합이 아닙니다. 프로토콜 상대(//cdn.example.com/a.js)는 페이지가 https면 https로 나가니, 그 호스트가 443을 여는지 따로 확인합니다.
프록시로 같은 출처의 https 경로에 외부 HTTP를 숨기는 방법은, 서버가 그 평문을 대신 받으므로 브라우저 혼합은 피합니다. 대신 서버가 사설망이나 메타데이터 주소를 따라가지 않게 호스트를 제한해야 합니다. 공개 페이지만 자물쇠를 맞추면 되는 경우에는 소스의 스킴 교체가 더 짧습니다. 한국 결제 화면처럼 스크립트가 하나라도 빠지면 버튼이 죽으니, 프리뷰 콘솔을 배포 체크에 넣으세요.
CORS 헤더로 이 경고가 풀리지 않음 | Access-Control-Allow-Origin은 다른 출처의 HTTPS 응답에 붙는 헤더입니다. 브라우저가 http 요청을 보내기 전에 자른 혼합에는 서버 헤더가 도착하지 않습니다.