TRACIO는 모든 방문자의 IP 주소를 서버 측에서 분석합니다. VPN, 프록시, Tor 출구 노드, 데이터센터 출처를 플래그로 표시하고, 도시 단위 지오로케이션을 확인합니다. 모든 분석은 클라이언트 측 의존성 없이 서버 측에서 실행됩니다.
IP 인텔리전스는 웹훅 페이로드를 통해 애플리케이션에 전달됩니다.
network 객체(VPN/프록시/Tor/데이터센터 불리언, 연결 유형, 그리고 Pro 이상에서는
proxy_detected)와 geo 객체(국가, 도시, 좌표, 시간대)가 모든 웹훅에서 전달됩니다.
ISP와 ASN, 그리고 real_ip 객체는 Business 이상에서 추가됩니다.
전체 페이로드 구조와 서명 검증은 Webhooks를 참조하세요.
VPN, 프록시, Tor, 데이터센터 분류는 연결하는 IP를 기준으로
상용 IP 인텔리전스 데이터베이스(VPN/프록시/Tor 유형은 IP2Location,
지오로케이션과 ASN은 MaxMind)를 사용해 확인됩니다. 이 네 가지 플래그는 각각 웹훅 network
객체에서 단일 불리언으로 노출되며, 공개 페이로드에는 방법별 세부 분류나
신뢰도 점수가 없습니다.
| 플래그 | 필드 | 의미 |
|---|---|---|
| VPN | network.vpn | IP가 알려진 VPN 제공업체에 속함 |
| 프록시 | network.proxy | IP가 데이터센터 또는 레지덴셜 프록시임 |
| Tor | network.tor | IP가 알려진 Tor 출구 노드임 |
| 데이터센터 | network.datacenter | IP가 클라우드/호스팅 제공업체에 속함 |
{ "network": { "vpn": true, "proxy": true, "tor": false, "datacenter": false, "connection_type": "VPN" }}// `payload` is the webhook delivery body (/docs/webhooks)if (payload.network.vpn) { // visitor is connecting through a VPN}데이터센터 프록시와 레지덴셜 프록시는 둘 다 network.proxy로 노출되며,
데이터센터 출처는 추가로 network.datacenter로 보고됩니다. 분류는
주요 클라우드/호스팅 제공업체(AWS, Google Cloud, Azure, Oracle Cloud,
Alibaba Cloud, DigitalOcean, Hetzner, OVH, Linode/Akamai, Vultr 등)를 포괄하는
IP2Location 프록시 데이터베이스에서 가져옵니다.
{ "network": { "vpn": false, "proxy": true, "tor": false, "datacenter": true, "connection_type": "DCH" }}network.proxy는 연결이 도착한 주소를 분류합니다. 이와 별개의 검사가 한 방문의 페이지
트래픽과 원시 네트워크 경로를 대조합니다. 둘이 서로 다른 네트워크를 통해 나가면 브라우저와
사이트 사이에 프록시나 VPN이 있는 것이며, 해당 방문은 network.proxy_detected: true로
보고됩니다. 같은 사업자의 주소 두 개(통신사 NAT, 동일한 VPN의 다른 출구)는 해당하지
않습니다.
Business 이상에서는 프록시나 VPN 뒤에서 관측된 공개 주소가, 연결이 도착한 주소와 나란히
국가 및 ISP와 함께 network.real_ip로 전달됩니다. 그러한 주소가 관측되지 않으면 이 필드는
없으므로 가드를 사용해 읽으세요(network.real_ip?.country). country와 isp는 확인할 수
없었던 경우에는 없습니다.
{ "network": { "proxy": false, "proxy_detected": true, "real_ip": { "address": "203.0.113.7", "country": "NL", "isp": "KPN" } }}Webhooks에서는 두 필드가 방문마다 같은 이름으로 전달됩니다.
Data API의 방문자 요약에서는 이를 집계하여
network.proxy_detected_seen(프록시가 앞단에 있었던 적이 있는지)과
network.last_real_ip(가장 최근 주소와 그 국가 및 ISP)로 제공합니다.
연결하는 IP에서 Tor 출구 노드가 일치하면 network.tor로 노출됩니다. 이와 별개로,
Tor 브라우저 자체는 네트워크 경로와 무관하게 안티핑거프린팅 설정을 통해
클라이언트 측에서 인식됩니다 —
Smart Signals를 참조하세요.
도시 단위 지오로케이션은 연결하는 IP(MaxMind)로부터 서버 측에서 확인되어
웹훅 geo 객체로 전달됩니다.
| 필드 | 타입 | 설명 | 플랜 |
|---|---|---|---|
geo.country | string | ISO 3166-1 alpha-2 국가 코드 | 전체 |
geo.city | string | 도시 이름 | 전체 |
geo.lat | number | 대략적인 위도 | 전체 |
geo.lon | number | 대략적인 경도 | 전체 |
geo.timezone | string | IANA 시간대 식별자 | 전체 |
{ "geo": { "country": "CZ", "city": "Prague", "lat": 50.05, "lon": 14.4, "timezone": "Europe/Prague" }}Business 및 Enterprise 플랜에서는 통신 사업자도 식별됩니다. 웹훅 페이로드에서는 두 필드
모두 network 객체에 있으며, ISP는 geo.isp로도 한 번 더 제공됩니다.
{ "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "isp": "Comcast Cable", "asn": 7922 }}Data API로 세션을 읽을 때는 배치가 조금 다릅니다. isp는 geo에만,
asn은 network에 있습니다. 같은 데이터에 표면이 둘일 뿐이므로, 사용하는 표면이 필드를
두는 위치에서 읽으세요.
Pro 이상에서는 웹훅이 velocity 블록을 함께 전달합니다. 방문자가 최근 얼마나
이동했는지를 나타냅니다.
| 필드 | 타입 | 설명 |
|---|---|---|
velocity.events5m | number | 최근 5분간 이 방문자의 이벤트 수 |
velocity.unique_ips | number | 이 방문자에게서 확인된 고유 IP 수 |
velocity.unique_locations | number | 이 방문자에게서 확인된 고유 위치 수 |
이 블록은 이벤트 시점에 방문자 카운터를 사용할 수 있을 때 포함됩니다(일반적으로
primary 단계). 블록이 없다는 것은 “데이터 없음”을 뜻하며 0을 뜻하지 않습니다 —
블록이 없다고 해서 “비정상적인 활동이 없다”고 해석하지 마세요.
IP 차단 목록 매칭은 공개 계약의 일부가 아닙니다. 웹훅 페이로드와 대시보드 모두
차단 목록 카테고리를 노출하지 않습니다. 네트워크 위험에 대한 웹훅 표면은 불리언
network 플래그, proxy_detected, Business 이상의 real_ip 객체, 연결 유형, 그리고 위의
벨로시티 카운터이므로, 위험 판단에는 이들을
decision 및 bot 필드(Smart Signals 참조)와 함께 사용하세요.
이 코드는 웹훅 페이로드의 network 객체를 직접 읽습니다. 전체 페이로드와
서명 검증은 Webhooks를 참조하세요.
// `payload` is the webhook delivery body (/docs/webhooks)function assessIPRisk(payload: WebhookPayload) { const { vpn, proxy, tor, datacenter } = payload.network
const risks: string[] = [] let riskLevel = "low"
if (tor) { risks.push("Tor exit node") riskLevel = "critical" }
if (vpn) { risks.push("VPN detected") riskLevel = riskLevel === "low" ? "medium" : riskLevel }
if (proxy) { risks.push("Proxy detected") riskLevel = riskLevel === "low" ? "medium" : riskLevel }
if (datacenter) { risks.push("Datacenter IP") riskLevel = riskLevel === "low" ? "medium" : riskLevel }
return { riskLevel, risks }}