From 6087217ab75a917a31e6c4724f1e6f0f6c651c4f Mon Sep 17 00:00:00 2001 From: Lucas Ricoy Date: Fri, 3 Jul 2026 12:24:09 -0300 Subject: [PATCH 1/2] fix: mirror django request user agent into $raw_user_agent --- .sampo/changesets/mirror-raw-user-agent.md | 5 +++++ posthog/integrations/django.py | 4 +++- posthog/test/integrations/test_middleware.py | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .sampo/changesets/mirror-raw-user-agent.md diff --git a/.sampo/changesets/mirror-raw-user-agent.md b/.sampo/changesets/mirror-raw-user-agent.md new file mode 100644 index 00000000..d0b0ff8d --- /dev/null +++ b/.sampo/changesets/mirror-raw-user-agent.md @@ -0,0 +1,5 @@ +--- +"posthog": patch +--- + +Django middleware also sends the request user agent as `$raw_user_agent`, the standardized property PostHog's server-side classification (e.g. bot detection) reads diff --git a/posthog/integrations/django.py b/posthog/integrations/django.py index 37cc7210..6eba5505 100644 --- a/posthog/integrations/django.py +++ b/posthog/integrations/django.py @@ -202,10 +202,12 @@ def _build_tags(self, request, user_id, user_email): if ip_address: tags["$ip"] = ip_address - # Extract user agent + # Extract user agent, mirrored into $raw_user_agent — the standardized + # property PostHog's server-side classification (e.g. bot detection) reads user_agent = request.headers.get("User-Agent") if user_agent: tags["$user_agent"] = user_agent + tags["$raw_user_agent"] = user_agent # Apply extra tags if configured if self.extra_tags: diff --git a/posthog/test/integrations/test_middleware.py b/posthog/test/integrations/test_middleware.py index 8b4d399d..22ce948c 100644 --- a/posthog/test/integrations/test_middleware.py +++ b/posthog/test/integrations/test_middleware.py @@ -90,6 +90,7 @@ def test_extract_tags_basic(self): headers={ "X-POSTHOG-SESSION-ID": "session-123", "X-POSTHOG-DISTINCT-ID": "user-456", + "User-Agent": "TestAgent/1.0", }, method="POST", path="/api/test", @@ -103,6 +104,8 @@ def test_extract_tags_basic(self): self.assertEqual(get_context_distinct_id(), "user-456") self.assertEqual(tags["$current_url"], "https://example.com/api/test") self.assertEqual(tags["$request_method"], "POST") + self.assertEqual(tags["$user_agent"], "TestAgent/1.0") + self.assertEqual(tags["$raw_user_agent"], "TestAgent/1.0") def test_extract_tags_missing_headers(self): """Test tag extraction when PostHog headers are missing""" From f721537690b16f8c51996d069935bd70fca1489a Mon Sep 17 00:00:00 2001 From: Lucas Ricoy <2034367+lricoy@users.noreply.github.com> Date: Tue, 7 Jul 2026 00:07:25 -0300 Subject: [PATCH 2/2] Update .sampo/changesets/mirror-raw-user-agent.md Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com> --- .sampo/changesets/mirror-raw-user-agent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.sampo/changesets/mirror-raw-user-agent.md b/.sampo/changesets/mirror-raw-user-agent.md index d0b0ff8d..975f1741 100644 --- a/.sampo/changesets/mirror-raw-user-agent.md +++ b/.sampo/changesets/mirror-raw-user-agent.md @@ -1,5 +1,5 @@ --- -"posthog": patch +"pypi/posthog": patch --- Django middleware also sends the request user agent as `$raw_user_agent`, the standardized property PostHog's server-side classification (e.g. bot detection) reads