예제
KO

프라이버시 모드

[privacy]는 페이지가 불러오는 외부 에셋(웹 폰트, CDN 스타일시트와 스크립트, 원격 이미지)을 빌드할 때 내려받아 사이트에서 직접 제공합니다. 그러면 방문자의 브라우저가 그 에셋 때문에 외부 호스트에 접속하지 않습니다. 로컬로 옮기는 대상에 나열된 태그만 해당하며, 그 밖에 페이지나 스크립트가 불러오는 것(제한 사항 참고)은 여전히 원래 호스트에서 받습니다.

주된 이유는 GDPR입니다. fonts.googleapis.com에서 Google Fonts를 불러오면 방문자마다 IP 주소가 Google로 전송되며, 독일 법원은 이에 동의가 필요하다고 판결했습니다. 폰트를 직접 호스팅하면 이 요청이 사라지고, 동의를 받을 필요도 없어집니다.

빠른 시작

[privacy]
enabled = true

이렇게 설정하면 템플릿의 Google Fonts 태그

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter&display=swap">

는 다음과 같이 출력됩니다.

<link rel="stylesheet" href="/assets/external/3f2a9c01b7de-css2.css">

로컬 스타일시트는 사용하는 .woff2 파일의 로컬 사본을 가리킵니다.

설정

[privacy]
enabled = false                 # 기본값은 꺼짐
include = []                    # 로컬로 옮길 호스트, 비우면 모든 외부 호스트
exclude = []                    # 절대 옮기지 않을 호스트, 예: ["www.youtube.com"]
output_dir = "assets/external"  # 빌드 출력 안의 디렉터리
cache_ttl = "7d"                # 요청 없이 다운로드를 재사용하는 기간
on_error = "warn-and-keep"      # warn-and-keep | fail
키 기본값 의미
enabled false 프라이버시 모드를 켭니다. 꺼져 있으면 출력이 바뀌지 않습니다.
include [] 로컬로 옮길 호스트. 정확히 일치해야 합니다(fonts.googleapis.com). 비우면 모든 외부 호스트가 대상입니다. 여기 적은 호스트는 네트워크 검사에서도 신뢰합니다(네트워크 안전 참고).
exclude [] 외부에 그대로 둘 호스트. include보다 우선합니다.
output_dir "assets/external" 내려받은 파일을 게시할 위치. URL에는 base_url의 하위 경로가 붙습니다.
cache_ttl "7d" 다운로드 캐시 수명. [[data.remote]]와 같은 기간 문법("90s", "12h", "7d")을 씁니다.
on_error "warn-and-keep" 캐시 사본 없이 다운로드가 실패했을 때: warn-and-keep은 경고 후 외부 URL을 그대로 두고, fail은 빌드를 멈춥니다.

로컬로 옮기는 대상

모든 페이지, 섹션, 페이지네이션 페이지, 택소노미 페이지, 404 페이지에서 다음 속성을 다시 씁니다.

다른 호스트를 가리키는 절대 http(s):// URL과 프로토콜 상대 // URL만 바꿉니다. 상대 URL, data: URL, base_url 호스트의 URL은 그대로 둡니다. HTML 주석과 인라인 <script>, <style> 본문도 바꾸지 않습니다.

내려받은 스타일시트도 다시 씁니다. @import와 url(...) 참조를 스타일시트 자신의 URL 기준으로 해석해 로컬로 옮기며, 네 단계까지 중첩을 따라갑니다. 이 참조는 스타일시트에 필요하므로 include와 상관없이 따라가지만, exclude는 그대로 적용됩니다. 외부에 남는 참조는 절대 URL로 다시 씁니다. image-set(...) 안의 문자열도 url(...)과 같이 처리합니다.

에셋을 로컬에서 제공하게 된 호스트로 향하는 <link rel="preconnect">와 <link rel="dns-prefetch"> 힌트는 지웁니다. 남겨 두면 브라우저가 여전히 그 호스트에 연결하기 때문입니다.

파일 형식

파일은 그 파일을 불러오는 태그에 맞는 확장자로만 게시하며, 확장자는 응답의 Content-Type(일반적인 형식이면 URL 경로)에서 정합니다. <img>, <source>, poster는 이미지, <video>와 <audio>는 오디오와 비디오, 스타일시트는 CSS, 스크립트는 JavaScript, 스타일시트 안의 참조는 폰트, 이미지(SVG 제외), CSS입니다. 그 밖의 경우(예: HTML로 응답한 <img>)는 경고와 함께 외부 URL을 그대로 둡니다. HTML이나 XML은 절대 게시하지 않습니다.

SVG는 <img> 계열 태그에서만 게시하며 경고를 출력합니다. SVG를 사이트에서 직접 열면 스크립트가 실행될 수 있습니다. 믿을 수 없는 호스트라면 exclude에 추가하세요. 스타일시트가 참조하는 SVG(background: url(icon.svg))는 게시하지 않으며, 계속 원래 호스트에서 불러옵니다.

Hwaro가 출력하는 태그

[markdown] math_engine = "mathjax"는 cdn.jsdelivr.net에서 MathJax를 불러오는데, MathJax는 폰트와 확장을 자기 URL 기준으로 불러오므로 외부에 그대로 둡니다. Mermaid([markdown] mermaid = true)는 인라인 모듈 import로 불러오며, 프라이버시 모드는 이를 다시 쓰지 않습니다. KaTeX와 highlight.js는 그대로 로컬로 옮깁니다.

게시되는 파일 이름은 <해시>-<이름>.<확장자>이며, 해시는 바이트의 SHA-256 앞 12자리입니다. 같은 파일은 한 번만 저장되고, 원본이 바뀌면 이름도 바뀌므로 브라우저가 오래된 사본을 쓰지 않습니다.

User-Agent

다운로드에는 데스크톱 브라우저 User-Agent를 보냅니다. Google Fonts는 이 값으로 폰트 형식을 고르는데, 알 수 없는 클라이언트에는 woff2 대신 훨씬 큰 TrueType을 보냅니다. 쿠키나 인증 정보는 보내지 않습니다.

네트워크 안전

프라이버시 모드가 내려받는 URL이 모두 직접 작성한 것은 아닙니다. 리다이렉트와 내려받은 스타일시트 안의 url(...) 참조는 외부에서 정합니다. 그래서 모든 요청과 모든 리다이렉트 단계를 먼저 검사합니다. 루프백, 사설(10/8, 172.16/12, 192.168/16, fc00::/7, 사이트 로컬 fec0::/10), 링크 로컬(169.254/16, fe80::/10), CGNAT(100.64/10), IETF 예약(192.0.0.0/24), 미지정, 멀티캐스트 주소로 해석되는 호스트는 거부하며, 이런 주소를 감싼 NAT64(64:ff9b::/96)나 IPv4 매핑 주소도 마찬가지입니다. 거부는 on_error를 따릅니다. 이렇게 해서 외부가 빌드에게 내부 서비스나 클라우드 메타데이터를 읽게 하고 그 응답을 사이트에 게시하게 만드는 것을 막습니다. 연결은 검사한 주소에 고정됩니다.

include에 적은 호스트는 이 검사를 건너뜁니다. 사내 CDN이나 로컬 테스트 서버는 이렇게 허용하세요. include는 두 가지 역할을 함께 합니다. 호스트를 하나라도 적으면 적은 호스트만 로컬로 옮깁니다. CDN 이름이 사설 주소로 해석되는 split-horizon DNS 환경이라면 필요한 호스트를 모두 적어야 하며, 스타일시트가 불러오는 호스트도 포함해야 합니다(Google Fonts CSS는 fonts.gstatic.com에서 불러옵니다).

실패한 다운로드는 같은 프로세스에서 10분 동안 다시 시도하지 않으므로, 응답하지 않는 호스트가 hwaro serve를 느리게 하는 것은 한 번뿐입니다. 파일마다 20MiB 상한이 있으며, 그보다 큰 파일(긴 동영상 등)은 경고와 함께 외부 URL을 유지합니다.

캐시와 오프라인 빌드

다운로드는 config.toml 옆의 .hwaro/external/에 보관되며, index.json에 URL마다 파일, 가져온 시각, Content-Type, SHA-256이 기록됩니다. 이 디렉터리는 빌드 캐시 밖, 그리고 hwaro serve가 감시하는 경로 밖에 있으므로 여기에 쓰는 일로 다시 빌드가 일어나지 않습니다.

hwaro serve도 같은 캐시를 쓰므로 다시 빌드할 때 네트워크에 접근하지 않습니다. 외부 호스트 상태와 상관없이 빌드하려면 CI에서 .hwaro/external/을 캐시하거나 커밋하세요. Hwaro는 .hwaro/ 안의 모든 것을 무시하는 .hwaro/.gitignore를 만들므로 git add -f .hwaro/external로 추가해야 합니다. 이 디렉터리의 오래된 다운로드는 지우지 않으니, 처음부터 다시 받으려면 디렉터리를 삭제하세요.

다시 쓰기는 페이지를 쓸 때, --minify 다음에 일어납니다. 따라서 --cache 빌드가 페이지를 건너뛰어도 이미 바뀐 HTML과 그 페이지가 쓰는 파일은 그대로 유지됩니다. 다운로드 실패로 외부 URL이 남은 페이지는 캐시하지 않으므로, 다음 --cache 빌드에서 다시 렌더링하고 다운로드를 재시도합니다.

다른 기능과의 관계

제한 사항

함께 보기