브라우저의 SameSite 쿠키 정책은 eTLD+1을 기준으로 same-site 여부를 판단합니다. TLD, eTLD, eTLD+1 각각의 정의와 관계를 정리합니다.
TLD(Top-Level Domain)는 도메인의 가장 오른쪽 레이블입니다. ICANN이 관리합니다.
example.com → TLD: com
example.co.kr → TLD: kr
example.org → TLD: org
eTLD(effective Top-Level Domain)는 도메인 등록이 불가능한 접미사 전체입니다. .com은 바로 아래에 도메인을 등록할 수 있지만, .kr은 .co.kr 같은 2차 구조를 거쳐야 등록됩니다. eTLD는 이런 깊이가 다른 도메인 구조를 동일한 레벨로 정규화합니다.
.com → 바로 아래에 등록 가능 → example.com
.co.kr → 바로 아래에 등록 가능 → example.co.kr
.kr → 바로 아래에 등록 불가 → co.kr은 누구의 도메인도 아님
| 도메인 | TLD | eTLD |
|---|---|---|
| example.com | com | com |
| example.co.kr | kr | co.kr |
| mysite.github.io | io | github.io |
| app.netlify.app | app | netlify.app |
| example.tokyo.jp | jp | tokyo.jp |
.com은 TLD와 eTLD가 동일합니다. .co.kr은 TLD는 .kr이지만 eTLD는 .co.kr입니다. github.io는 TLD가 .io지만, GitHub이 서브도메인을 사용자별로 할당하는 구조이므로 github.io 전체가 eTLD입니다.
eTLD+1은 eTLD 바로 왼쪽에 레이블 하나를 추가한 것입니다. 브라우저가 인식하는 도메인 소유권 경계이자 "사이트" 단위입니다.
| 전체 도메인 | eTLD | eTLD+1 |
|---|---|---|
| www.example.com | com | example.com |
| api.example.com | com | example.com |
| shop.example.co.kr | co.kr | example.co.kr |
| user123.github.io | github.io | user123.github.io |
| my-app.netlify.app | netlify.app | my-app.netlify.app |
www.example.com과 api.example.com은 eTLD+1이 example.com으로 동일합니다. 같은 소유자의 도메인이므로 같은 사이트입니다.
user123.github.io와 user456.github.io는 eTLD+1이 다릅니다. github.io가 eTLD로 등록되어 있으므로 각각 별개의 사이트로 격리됩니다.
브라우저의 SameSite 판단은 두 URL의 eTLD+1 비교입니다.
https://www.example.com → eTLD+1: example.com
https://api.example.com → eTLD+1: example.com
→ same-site ✓
https://example.com → eTLD+1: example.com
https://evil.com → eTLD+1: evil.com
→ cross-site ✗
https://a.github.io → eTLD+1: a.github.io
https://b.github.io → eTLD+1: b.github.io
→ cross-site ✗
서브도메인은 eTLD+1이 동일하므로 항상 same-site입니다. evil.sub.example.com과 bank.example.com은 eTLD+1이 example.com으로 같아 SameSite=Lax를 설정해도 쿠키가 전송됩니다.
eTLD 목록은 Mozilla가 관리하는 Public Suffix List(PSL)에서 정의됩니다. 모든 주요 브라우저가 이 목록을 사용합니다.
PSL 항목은 두 종류로 구분됩니다.
ICANN 도메인 — 국가·일반 최상위 도메인과 그 하위 구조입니다.
// com
com
// kr
kr
co.kr
or.kr
go.kr
// jp
jp
tokyo.jp
osaka.jp
PRIVATE 도메인 — 기업이 사용자에게 서브도메인을 할당하는 서비스입니다.
// GitHub Pages
github.io
// Netlify
netlify.app
// Vercel
vercel.app
// AWS S3
s3.amazonaws.com
PSL에 github.io가 등록되어 있으므로 username.github.io는 각각 독립된 eTLD+1로 취급됩니다.
자체 서비스에서 사용자별 서브도메인을 발급하는 경우(예: username.myservice.com), PSL의 PRIVATE 섹션에 등록하면 각 서브도메인이 독립된 eTLD+1로 취급됩니다. 등록 절차는 GitHub 저장소에서 Pull Request를 제출하는 방식입니다.
1. 자격 확인 — PRIVATE 섹션 등록 요청은 반드시 도메인 소유자가 직접 제출해야 합니다. 제3자가 대신 제출하는 것은 거부됩니다. ICANN 섹션 변경은 해당 레지스트리, ICANN 또는 IANA에서 요청해야 합니다.
2. 가이드라인 확인 — GitHub Wiki의 Guidelines에서 포맷, 정렬 규칙, 수락 기준을 확인합니다. 항목은 TLD 기준 알파벳순으로 정렬되며, 새 항목은 올바른 위치에 삽입해야 합니다.
// My Service : https://myservice.com
// Submitted by Admin <admin@myservice.com>
myservice.com
3. DNS 검증 레코드 추가 — 등록하려는 각 도메인에 _psl TXT 레코드를 추가합니다. 값은 제출한 PR의 URL입니다.
$ dig TXT _psl.myservice.com
# "https://github.com/publicsuffix/list/pull/1234"
이 레코드는 PR 승인 후에도 유지해야 합니다. 향후 자동화를 통해 _psl 레코드가 없는 항목은 제거 대상이 될 수 있습니다.
4. PR 제출 및 테스트 — Pull Request를 생성하면 CI에서 자동으로 테스트가 실행됩니다. 테스트를 통과하지 못한 PR은 거부됩니다. 로컬에서 make test로 미리 검증할 수 있습니다.
다음 목적의 등록은 거부됩니다.
PSL 등록은 전역적 영향을 가집니다. 모든 주요 브라우저가 이 목록을 사용하므로, 등록되면 해당 도메인의 서브도메인 간 쿠키 공유가 차단되고 각각 독립된 사이트로 취급됩니다. 등록 후 철회는 어렵고 우선순위가 낮으므로 신중하게 결정해야 합니다. 또한 브라우저 반영까지 수개월이 소요될 수 있습니다.
도메인: blog.shop.example.co.kr
TLD → kr (ICANN이 관리하는 최상위)
eTLD → co.kr (등록 불가능한 접미사 전체)
eTLD+1 → example.co.kr (브라우저가 판단하는 "사이트" 단위)
나머지 → blog.shop (같은 사이트 내의 서브도메인)
.com, .co.kr, .github.io를 동일 레벨로 취급합니다.