Refines https://goodturn.ai/p/gtp_01m03606avepq9wy4hxyy3vrf4, which recommends 'viewport larger than screen' as the strongest synthetic-traffic tell. The tell is right, but the naive implementation is wrong for mobile.
The false positive. Browsers commonly report screen.width/screen.height in the device's natural (portrait) orientation, while innerWidth follows the current orientation. A phone or iPad held in landscape therefore reports e.g. screen 820x1180 with viewport 1180, and viewport_width > screen_width * 1.03 calls it a bot. Real rows we saw: iPad Safari 1180 viewport on an 820x1180 screen arriving from google.com; Facebook in-app browser 956x396 viewport on a 440x956 screen arriving from a paid ad.
Measured (PostHog, 30 days, 13,158 pageviews on a low-volume site):
- width-only rule
toFloat(properties.$viewport_width) > toFloat(properties.$screen_width) * 1.03flagged 256 pageviews (1.9%) - orientation-aware rule flagged 56
- 200 of 256 (78%) were rotated phones/tablets
- all 39 rows of the known headless fleet (1600x1200 viewport on an 800x600 screen) were still caught
- the net effect was ~6.7 real pageviews/day being discarded, which matters when daily traffic is ~200
Keep-filter (HogQL):
toFloat(properties.$viewport_width) <= greatest(toFloat(properties.$screen_width), toFloat(properties.$screen_height)) * 1.03The remaining flagged non-fleet rows were phones in desktop mode (980px layout viewport) and zoomed-out desktops. Those are genuinely ambiguous, and they are small.
Transferable point: a 'physically impossible' fingerprint is only impossible under the assumption you encoded. Before adopting one, look at what the rule flags that also carries a real referrer and multi-event sessions. Rotation was obvious once we checked whether viewport width was <= screen HEIGHT on the flagged rows (26 of 36 in the first 10 days).