사전 사용자 에이전트 문자열
포맷

사용자 에이전트 문자열

gabury1고친 사람 github-actions[bot]

사용자 에이전트 문자열은 요청을 보내는 프로그램이 서버에게 자기를 소개하는 한 줄입니다. 브라우저는 요청을 보낼 때마다 이 줄을 함께 싣습니다. 서버는 이 줄을 읽고 어떤 프로그램이 보낸 요청인지 짐작합니다. 보내는 쪽이 마음대로 적는 값이라 참고로만 씁니다.

쉽고 빠른 이해

요청을 보낸 프로그램이 스스로 적어 보내는 자기소개입니다. 크롬 브라우저라면 운영체제와 브라우저 이름, 버전을 한 줄에 적어 보냅니다.

서버는 요청만 보고는 누가 보냈는지 모릅니다. 이 줄이 없으면 특정 브라우저에서만 나는 오류를 골라 피할 수 없습니다. 방문자가 무엇으로 들어오는지 셀 수도 없습니다. 검색 로봇이 왔는지 가리기도 어렵습니다.

이렇게 돕니다.

  1. 브라우저가 요청마다 이 줄을 헤더에 실어 보낸다
  2. 줄은 「이름/버전」 조각과 괄호 속 설명으로 이루어진다
  3. 서버는 줄을 읽어 로그에 남기거나 응답을 조금 바꾼다

대가는 믿을 수 없다는 점입니다. 보내는 쪽이 무엇이든 적을 수 있어서 권한을 가르는 데는 못 씁니다. 줄이 자세할수록 사용자를 알아보는 단서가 됩니다. 그래서 요즘 브라우저는 내용을 일부러 줄여 보냅니다.

상세

이 절은 사용자 에이전트 문자열이 어디에 실려 오고 무엇으로 이루어지는지부터 봅니다. 그다음 브라우저가 보내는 한 줄을 조각내 읽고, 서버가 이 값을 어디까지 믿어도 되는지 짚습니다.

사용자 에이전트는 사용자를 대신해 요청을 내는 프로그램입니다. 브라우저가 가장 흔합니다. 명령줄 도구, 검색 엔진의 크롤러, 모바일 앱도 여기에 듭니다. 사용자 에이전트 문자열은 이 프로그램이 자기 이름과 버전을 적은 텍스트입니다.

User-Agent 헤더

이 문자열은 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 규약) 요청의 User-Agent 헤더에 실려 옵니다. HTTP 헤더는 요청 앞머리에 붙는 「이름: 값」 꼴의 줄들입니다. 요청이 무엇을 원하고 누가 보냈는지 같은 부가 정보를 담습니다.

http
GET /index.html HTTP/1.1
Host: example.com
User-Agent: curl/8.5.0

마지막 줄의 콜론 뒤가 사용자 에이전트 문자열입니다. 명령줄 도구 curl 이 보낸 요청이라 이름과 버전 한 조각으로 끝납니다.

헤더는 값을 나르는 그릇입니다. 문자열은 그 안에 든 값입니다.

이 값은 헤더 밖에서도 읽힙니다. 브라우저에서 도는 JavaScript 프런트엔드 코드는 같은 값을 navigator.userAgent 로 읽습니다. 서버 코드와 프런트엔드 코드가 같은 문자열을 보고 따로 판단하는 일이 생기는 까닭입니다.

문자열을 이루는 조각

문자열은 두 가지 조각으로 이루어집니다. 하나는 제품 토큰이고 다른 하나는 주석입니다. 조각과 조각 사이는 공백으로 띄웁니다.

제품 토큰은 이름/버전 꼴입니다. 슬래시 앞이 제품 이름이고 뒤가 버전입니다. 버전은 빼도 됩니다. 이름과 버전 안에는 공백을 쓸 수 없습니다. 공백이 조각을 가르는 구분자이기 때문입니다.

주석은 괄호로 감싼 설명입니다. 괄호 안에서는 공백을 자유롭게 씁니다. 운영체제 이름처럼 제품 토큰 꼴에 안 맞는 정보를 여기에 적습니다. 괄호 안을 세미콜론으로 나누는 것은 문법이 아니라 브라우저들이 따르는 관례입니다.

조각을 늘어놓는 순서에도 원래 관례가 있습니다. 앞에 올수록 중요한 제품을 적습니다. 첫 조각이 그 프로그램 자신입니다. 뒤로 가며 그 안에 든 부품을 적습니다.

브라우저가 보내는 한 줄

윈도우 데스크톱의 크롬 브라우저가 보내는 문자열입니다. 조각마다 줄을 바꿔 적었습니다. 보낼 때는 공백으로 이어진 한 줄입니다.

Mozilla/5.0
(Windows NT 10.0; Win64; x64)
AppleWebKit/537.36
(KHTML, like Gecko)
Chrome/143.0.0.0
Safari/537.36

여섯 조각 가운데 제품 토큰이 넷, 주석이 둘입니다. 조각마다 뜻을 표로 풀면 이렇습니다.

조각 종류 뜻
Mozilla/5.0 제품 토큰 거의 모든 브라우저가 붙이는 호환 표시
(Windows NT 10.0; Win64; x64) 주석 운영체제와 프로세서 종류
AppleWebKit/537.36 제품 토큰 화면을 그리는 엔진
(KHTML, like Gecko) 주석 두 엔진의 이름을 빌린 호환 표시
Chrome/143.0.0.0 제품 토큰 브라우저 자신과 버전
Safari/537.36 제품 토큰 사파리와 비슷하다는 호환 표시

브라우저 자신을 가리키는 조각은 다섯째의 Chrome 하나뿐입니다. 앞에서 본 관례대로라면 첫 조각이 브라우저 자신이어야 합니다. 브라우저는 이 관례를 지키지 않습니다. 첫 조각이 호환 표시라서 자신은 뒤쪽에 있습니다.

표에 나온 엔진은 웹 페이지를 화면에 그리는 부품입니다. 이 부품을 렌더링 엔진이라고 부릅니다. Gecko 는 파이어폭스의 엔진입니다. KHTML(Konqueror 라는 리눅스 브라우저의 엔진)은 WebKit 이 갈라져 나온 옛 엔진입니다.

크롬의 지금 엔진은 WebKit 에서 갈라져 나온 Blink 입니다. 그런데도 문자열에는 AppleWebKit 이라는 옛 이름이 남아 있습니다. 까닭은 다음 소절에서 볼 Mozilla 와 같습니다.

모든 브라우저가 Mozilla 로 시작하는 까닭

초기 웹에서 Mozilla 는 넷스케이프 브라우저가 쓰던 이름이었습니다. 당시 서버들은 문자열에서 이 이름을 찾아 기능이 많은 페이지를 보냈습니다. 이름이 없는 브라우저는 기능을 뺀 페이지를 받았습니다.

뒤에 나온 브라우저들은 같은 페이지를 받으려고 자기 문자열 앞에 Mozilla 를 적었습니다. 새 브라우저가 나올 때마다 같은 일이 되풀이되었습니다. like Gecko 와 Safari 조각도 그렇게 붙은 호환 표시입니다.

그 결과 이 문자열은 남의 이름이 여럿 섞인 자기소개가 되었습니다. 첫 조각이 그 프로그램 자신이라는 관례도 이렇게 깨졌습니다. 문자열에 Safari 가 있다고 사파리 브라우저로 판단하면 틀립니다. 크롬도 이 조각을 보내기 때문입니다.

서버가 이 문자열로 하는 일

서버가 이 값을 쓰는 곳은 크게 셋입니다. 아래 표는 쓰임마다 예를 하나씩 보입니다.

쓰임 예
기록과 통계 접근 로그에 요청마다 남겨 방문자의 브라우저·기기 비율을 센다
호환성 우회 문자열에 Chrome/143 이 들어 있을 때만 그 버전에서 깨지는 기능을 끄고 보낸다
보낸 쪽 구분 크롤러나 웹뷰에서 온 요청을 가려 낸다

크롤러는 자기 이름을 제품 토큰으로 적어 보냅니다. robots.txt 는 사이트가 크롤러에게 들어오지 말 경로를 적어 두는 파일입니다. 이 파일은 크롤러를 그 제품 토큰 이름으로 가리킵니다.

웹뷰는 앱 화면 안에 박아 넣은 브라우저입니다. 안드로이드 웹뷰는 괄호 주석 안에 wv 를 붙여 보냅니다. 서버는 이 표시를 보고 앱 안에서 연 화면인지 가립니다.

사용자 에이전트 탐지

문자열을 읽어 브라우저를 가리고 응답을 바꾸는 일을 사용자 에이전트 탐지라고 합니다. 앞에서 본 것처럼 문자열에 남의 이름이 섞여 있어 판단이 쉽게 틀립니다. 새 브라우저나 새 버전이 나오면 탐지 규칙도 따라 고쳐야 합니다.

그래서 프런트엔드 코드는 브라우저 이름 대신 필요한 기능이 있는지를 직접 확인합니다. 이 방식이 기능 감지입니다. 예를 들어 navigator.clipboard 가 있는지 먼저 봅니다. 있으면 그것으로 복사합니다. 없으면 다른 방법으로 복사합니다.

문자열에 따라 응답을 바꾸면 캐시도 챙겨야 합니다. 캐시는 받은 응답을 저장해 두었다가 같은 요청에 다시 내주는 장치입니다. 캐시가 이 사정을 모르면 한 브라우저용 응답이 다른 브라우저에 갑니다.

이것을 막으려고 응답의 Vary 헤더에 User-Agent 를 적습니다. 요청의 User-Agent 값이 다르면 다른 응답으로 치라는 뜻입니다. 대가로 캐시에 같은 페이지가 문자열 종류만큼 따로 쌓입니다.

믿을 수 없는 값

사용자 에이전트 문자열은 보내는 쪽이 적는 값입니다. 서버는 그것이 참인지 확인할 방법이 없습니다. 명령줄 도구도 옵션 하나로 브라우저를 흉내 냅니다.

터미널
curl -A "Mozilla/5.0" https://example.com/

이 요청은 서버 로그에 브라우저가 보낸 것처럼 남습니다. 데이터를 긁어 가는 봇이 브라우저 문자열을 흉내 내는 일도 흔합니다. 구글 검색 로봇의 이름인 Googlebot 을 빌려 쓰는 요청도 있습니다.

그래서 이 값은 권한이나 보안 판단의 근거로 쓰지 않습니다. 「우리 앱에서 온 요청만 허용」을 이 문자열로 가르면 누구나 그 문자열을 적어 들어옵니다.

누가 보냈는지 확인해야 하면 인증을 씁니다. 인증은 요청을 보낸 쪽이 스스로 밝힌 그 사람이 맞는지 확인하는 일입니다. 비밀번호나 서명된 토큰처럼 흉내 낼 수 없는 증거를 받습니다.

줄어드는 문자열

문자열이 자세할수록 사용자를 알아보기 쉬워집니다. 운영체제 세부 버전, 기기 모델명, 브라우저의 잔 버전을 합치면 같은 값을 보내는 사람이 몇 안 됩니다.

이렇게 작은 정보를 모아 사용자를 알아보는 일을 브라우저 핑거프린팅이라고 합니다. 사용자 에이전트 문자열은 그 정보 가운데 하나입니다. 요청마다 실려 오니 모으기도 쉽습니다.

사람을 알아보는 흔한 수단은 쿠키입니다. 쿠키는 서버가 브라우저에 맡겨 두는 작은 값이라 사용자가 지울 수 있습니다. 핑거프린팅은 브라우저에 아무것도 맡기지 않아 지울 것도 없습니다. 쿠키 없이도 여러 사이트에 걸쳐 같은 사람을 따라갈 수 있어 개인정보 문제가 됩니다.

브라우저는 필요 이상으로 자세한 정보를 문자열에 넣지 않는 편이 좋습니다. 개인정보를 지키려고 헤더를 아예 빼도 됩니다.

요즘 브라우저는 문자열 일부를 고정값으로 바꿔 보냅니다. 이것을 사용자 에이전트 축소(user-agent reduction)라고 부릅니다. 안드로이드 폰의 크롬이 보내는 값을 줄이기 전과 뒤로 견주면 이렇습니다.

정보 줄이기 전 줄인 뒤
운영체제 버전 Android 16 Android 10
기기 모델 Pixel 9 K
브라우저 버전 143.0.12.45 143.0.0.0

줄인 뒤의 값은 기기와 상관없이 늘 같습니다. 브라우저 버전은 맨 앞 번호만 참값입니다. 뒤는 0 으로 채웁니다. 앞에서 본 Chrome/143.0.0.0 이 0 으로 끝난 까닭입니다.

필요한 만큼만 묻는 Client Hints

줄여서 빠진 정보가 서버에 꼭 필요할 때가 있습니다. 그때는 따로 요청해서 받습니다. 이 방식을 Client Hints라고 합니다. 여기에 쓰는 헤더 이름의 CH 는 Client Hints 의 줄임입니다.

주고받는 헤더는 둘입니다. 서버는 응답의 Accept-CH 헤더에 더 알고 싶은 항목을 적습니다. 브라우저는 그 항목을 Sec-CH-UA 로 시작하는 헤더에 담아 보냅니다. UA 는 User Agent(사용자 에이전트)의 줄임입니다.

sequenceDiagram
    participant 브라우저
    participant 서버
    브라우저->>서버: 첫 요청 · 줄인 문자열만 보낸다
    서버-->>브라우저: 응답 · Accept-CH 에 원하는 항목을 적는다
    브라우저->>서버: 다음 요청 · Sec-CH-UA 헤더를 더 붙인다

예를 들어 Sec-CH-UA-Platform-Version 헤더는 운영체제 세부 버전을 담습니다. 서버가 묻지 않은 항목은 보내지 않습니다. 사이트마다 필요한 만큼만 알려지는 셈입니다.

이 헤더들도 문자열처럼 보내는 쪽이 적는 값입니다. 그래서 믿을 수 없습니다.

응답에 붙는 Server 헤더

응답에는 같은 꼴의 Server 헤더가 있습니다. 응답을 만든 서버 소프트웨어가 제품 토큰과 주석으로 자기를 적습니다. 문법이 사용자 에이전트 문자열과 같습니다.

버전이 드러나면 공격자가 그 버전의 알려진 약점을 고르기 쉬워집니다. 그래서 운영 서버는 이 헤더에서 버전을 빼거나 헤더를 아예 안 보내기도 합니다.

관련 항목

이 문자열을 싣는 헤더

User-Agent 헤더 · HTTP 헤더 · Server 헤더 · Vary 헤더 · HTTP

이 문자열을 보내는 프로그램

사용자 에이전트 · 브라우저 · 크롤러 · 웹뷰 · 헤드리스 브라우저 · 명령줄 HTTP 클라이언트 · curl

이 문자열을 이루는 구성 요소

product token · 렌더링 엔진 · WebKit · Blink · Gecko

이 문자열을 읽어 응답을 가르는 방식

사용자 에이전트 탐지 · 콘텐츠 협상 · robots.txt · 접근 로그 · 캐시

이 문자열을 대신할 수 있는 다른 수단

Client Hints · Sec-CH-UA · Accept-CH · 기능 감지 · 인증

이 문자열이 낳는 개인정보 문제

브라우저 핑거프린팅 · 사용자 에이전트 축소 · 쿠키 · 웹 추적

다른 이름: user agent string · user-agent string · UA 문자열 · UA string · 유저 에이전트 문자열