SameSite 쿠키 설정 방법, 속성 6개로 정리

document.cookie에 HttpOnly를 아무리 적어 넣어도 실제 쿠키에는 반영되지 않는 이유는 실수가 아니라 사양입니다. HttpOnly는 애초에 브라우저 자바스크립트로는 설정할 수 없고, 서버가 보내는 HTTP 응답 헤더에서만 유효한 속성이기 때문입니다.
이 글은 Domain, Path, Max-Age, SameSite, Secure, HttpOnly 여섯 속성이 쿠키 문자열 안에서 실제로 어떻게 조합되는지, 그리고 document.cookie 대입문과 Set-Cookie 헤더가 왜 서로 다른 결과물을 만드는지를 코드 동작 그대로 정리합니다.
요약 ① HttpOnly는 서버의 Set-Cookie 헤더에서만 유효하며, document.cookie로는 절대 설정할 수 없습니다. ② Domain과 Path는 쿠키가 전송되는 범위를, Max-Age는 초 단위로 만료 시점을 결정합니다. ③ 쿠키 문자열을 파싱할 때는 세미콜론(;)으로 구간을 나누고, 각 구간을 등호(=) 기준 첫 번째 위치에서만 잘라야 값 안의 "="이 깨지지 않습니다.
세션 쿠키를 만들 때 자주 막히는 지점
로그인 세션 쿠키를 직접 만들어보면 자주 겪는 상황이 있습니다. 브라우저 개발자 도구 콘솔에 document.cookie = "session=abc123; HttpOnly"를 입력하고 다시 document.cookie를 조회해도, HttpOnly라는 글자는 어디에도 남아 있지 않습니다.
오타도 아니고 브라우저 버그도 아닙니다. document.cookie는 자바스크립트 실행 컨텍스트 안에서만 동작하는데, HttpOnly는 정의 자체가 "자바스크립트가 이 쿠키를 읽거나 쓰지 못하게 한다"는 속성입니다.
스크립트가 스스로에게 이 제한을 걸 수 있다면 제한의 의미가 사라지므로, 브라우저는 document.cookie로 들어온 HttpOnly 표기를 조용히 무시합니다.
SameSite=None 쿠키가 전송되지 않는 경우
비슷하게 자주 걸리는 경우가 크로스사이트 요청에서 쿠키가 빠지는 상황입니다. 결제 위젯이나 외부 iframe에서 SameSite를 None으로 지정했는데도 쿠키가 전송되지 않는다면, 대부분 Secure 속성을 함께 넣지 않은 경우입니다.
최신 브라우저는 SameSite=None인 쿠키에 Secure가 없으면 그 쿠키 자체를 저장하지 않거나 전송하지 않습니다.
도구 없이 쿠키 문자열을 직접 만드는 방법
쿠키 문자열은 규칙만 알면 손으로도 정확히 만들 수 있습니다. 순서는 이렇습니다.
- key=value부터 인코딩합니다. 쿠키 문자열은 세미콜론(;)과 등호(=)를 구분자로 쓰기 때문에, 값 안에 이 문자나 공백·한글이 들어가면 URL 인코딩을 거쳐야 문자열이 깨지지 않습니다.
- 필요한 속성만 "; "로 이어 붙입니다.
Domain=example.com,Path=/,Max-Age=3600처럼 이름=값 형태로 붙이고, 값이 없는 속성(Secure, HttpOnly)은 이름만 적습니다. - HttpOnly는 목적지가 다릅니다. 만든 문자열을 자바스크립트로 실행하고 싶다면
document.cookie = "..."형태로 대입하되, 이 경우 HttpOnly는 애초에 넣어도 무시되는 서버 헤더 전용 속성입니다. 서버 응답으로 내려보내고 싶다면 HTTP 헤더에Set-Cookie: ...형태로 그대로 넣으면 되고, 이때는 HttpOnly도 정상적으로 적용됩니다.
Max-Age, Domain, Path 값을 정하는 기준
Max-Age와 Expires 중 하나만 고르면 됩니다. Max-Age는 지금 시점부터 몇 초 후에 만료되는지를 정수로 적고, 값을 생략하면 브라우저 창을 닫을 때 사라지는 세션 쿠키가 됩니다. Expires는 절대 날짜를 문자열로 적어야 해서 형식을 맞추는 부담이 있어 Max-Age가 더 흔히 쓰입니다.
Domain을 생략하면 지금 접속한 호스트에만 정확히 묶이는 호스트 전용 쿠키가 되어 서브도메인으로는 전송되지 않습니다. Domain=example.com처럼 명시하면 example.com은 물론 sub.example.com 같은 서브도메인 요청에도 쿠키가 함께 실립니다.
Path는 값을 생략하면 브라우저가 쿠키를 만든 요청의 경로를 바탕으로 기본값을 계산합니다. Path=/로 명시하면 도메인 아래 모든 경로에서 쿠키가 전송되고, Path=/account처럼 좁히면 그 경로와 하위 경로에서만 전송됩니다. 이 계산을 저희 도구로 바로 확인할 수 있습니다.
Key/Value와 Domain, Path, Max-Age, SameSite, Secure, HttpOnly를 입력하면 document.cookie 문과 Set-Cookie 헤더 문자열이 동시에 생성됩니다.

자주 헷갈리는 부분
SameSite와 Secure는 한 세트로 움직입니다. SameSite=None을 쓴다는 건 크로스사이트 요청에도 쿠키를 실어 보내겠다는 뜻인데, 그만큼 쿠키가 노출될 여지도 커집니다. 그래서 브라우저는 Secure 없이 SameSite=None만 있는 쿠키를 거부합니다.
HttpOnly 체크박스를 켜도 document.cookie 쪽 출력 줄에는 흔적이 남지 않는 것이 정상입니다. HttpOnly는 자바스크립트가 접근하지 못하게 막는 속성이라, 자바스크립트로 실행할 문자열 자체에 넣는 것이 애초에 의미가 없기 때문입니다.
| 속성 | 정하는 것 | 값 형식 | 생략하면 |
|---|---|---|---|
| Domain | 쿠키를 보낼 호스트 범위 | example.com | 접속한 정확한 호스트에만 전송(서브도메인 제외) |
| Path | 쿠키를 보낼 URL 경로 범위 | /account | 쿠키를 만든 요청 경로 기준으로 제한 |
| Max-Age | 몇 초 후 만료되는지 | 3600(초 단위) | 세션 쿠키(브라우저 종료 시 삭제) |
| SameSite | 다른 사이트 요청에도 실어 보낼지 | Strict / Lax / None | 브라우저마다 다르나 최신 브라우저 다수는 Lax로 취급 |
| Secure | HTTPS 연결에서만 전송할지 | 값 없이 이름만 표기 | HTTP 연결에서도 전송 |
| HttpOnly | 자바스크립트가 쿠키를 읽을 수 있는지 | 값 없이 이름만 표기, 헤더 전용 | document.cookie로 읽기 가능 |
쿠키 문자열을 파싱할 때 놓치기 쉬운 부분
브라우저 개발자 도구에서 복사한 값 앞에는 Cookie: 접두사가 붙어 있을 때가 있습니다. 파싱 전에 이 접두사부터 떼어내야 첫 번째 키가 Cookie 자체로 잘못 인식되지 않습니다.
각 구간을 등호(=) 기준으로 나눌 때는 첫 번째 등호에서만 잘라야 합니다. 토큰 값 자체에 등호(=)가 패딩 문자로 들어가는 경우가 있는데, 등호가 나올 때마다 잘라버리면 값이 중간에서 끊어집니다.
정리
- Domain과 Path는 쿠키가 전송되는 범위를 정하고, Max-Age는 초 단위로 만료 시점을 정합니다.
- SameSite=None을 쓰려면 Secure를 함께 켜야 브라우저가 쿠키를 거부하지 않습니다.
- HttpOnly는 오직 서버의 Set-Cookie 헤더에서만 유효하며, document.cookie로는 설정도 해제도 할 수 없습니다.
- 쿠키 문자열을 파싱할 때는
Cookie:접두사를 먼저 떼고, 세미콜론으로 나눈 뒤 각 구간을 첫 번째 등호에서만 잘라야 값이 깨지지 않습니다.
Key/Value와 Domain, Path, Max-Age, SameSite, Secure, HttpOnly를 입력하면 document.cookie 문과 Set-Cookie 헤더 문자열이 동시에 생성됩니다.
자주 묻는 질문
HttpOnly 쿠키를 자바스크립트로 읽거나 수정할 수 있나요?
없습니다. HttpOnly는 정의상 자바스크립트가 접근하지 못하게 막는 속성이라, document.cookie로 조회해도 그 쿠키는 목록에 나타나지 않고 값을 덮어써도 적용되지 않습니다. HttpOnly 쿠키는 오직 서버가 HTTP 응답의 Set-Cookie 헤더로만 설정하거나 변경할 수 있습니다.
SameSite=None으로 설정했는데 쿠키가 전송되지 않습니다. 왜 그런가요?
Secure 속성이 함께 있는지 먼저 확인해야 합니다. 최신 브라우저는 SameSite=None인 쿠키에 Secure가 빠져 있으면 그 쿠키를 저장하지 않거나 전송하지 않습니다. HTTPS 환경에서 Secure를 함께 켜면 해결됩니다.
Max-Age와 Expires는 어떤 차이가 있나요?
Max-Age는 지금부터 몇 초 후에 만료되는지를 정수로 적는 상대적 방식이고, Expires는 만료 시각을 절대 날짜 문자열로 적는 방식입니다. 둘 다 값을 지정하지 않으면 세션 쿠키가 되어 브라우저를 닫을 때 삭제됩니다.
쿠키 값에 한글이나 공백을 그대로 넣어도 되나요?
그대로 넣으면 세미콜론(;)이나 등호(=), 공백 같은 구분자와 충돌해 문자열이 깨질 수 있습니다. URL 인코딩을 거쳐 안전한 문자로 바꾼 뒤 쿠키 문자열에 넣고, 값을 읽을 때 다시 디코딩하는 것이 안전합니다.
