·
Last changedVersion:
This version passed the hardening gate. Hardening applied 7 targeted changes and preserved all other text. Classified findings before hardening: 0. Classified findings closed: 0. Classified findings remaining: 0. Unclassified scanner findings remaining: 0. Review findings closed: 6. Review findings remaining: 9.
None
None
Expand | File | Group | Subtype | Severity | Status |
|---|
No findings available
0 findings
Path cleaning rules normalize $pathname and $entry_pathname so that pages
sharing the same template (/users/123/profile, /users/456/profile, …) collapse
into one row (/users/<id>/profile) in Web analytics tiles, Paths insights, and
any HogQL query that calls apply_path_cleaning. They are the right answer when
a breakdown is fragmented across thousands of near-identical URLs.
This skill teaches you how to:
regex + alias rules in re2 syntax with the project's placeholder
conventionTeam.path_cleaning_filters is a JSON list of PathCleaningFilter objects:
regex — a re2 pattern. No
need to escape /. Anchor with ^ / $ when you mean it.alias — the replacement. Default to angle-bracket placeholders
(<id>, <slug>, <uuid>, <date>) so the cleaned path stays
human-readable. Backreferences to the regex's capture groups also work, but
they cost more to evaluate — see the backreference pitfall below before
reaching for one.order — integer. Rules apply sequentially in order ascending,
each rule's output feeds the next.Application is replaceRegexpAll(pathname, regex, alias) per rule, chained.
Ask yourself: is the user complaining about cardinality (too many distinct paths
in a chart), or do they want a per-URL drill-down? Path cleaning is for the
former. If they want per-URL data, suggest a property filter on $pathname
instead.
Don't guess at patterns — query them. With the execute-sql MCP tool:
Scan the result for:
/users/123, /orders/4242/sessions/8f3c1a3b-…/posts/why-i-love-posthog/archive/2024-09-12/en-US/, /fr-FR//page/3/| Pattern | Example match | regex | alias |
|---|---|---|---|
Numeric segment |
|
|
|
UUID v4 |
|
|
|
Slug |
|
|
|
ISO date |
|
|
|
Locale prefix |
|
|
|
Anchoring rules of thumb:
^ only when the segment must be at the beginning of
the path$ to keep a generic rule (e.g. \d+$) from matching mid-path
segmentspick one:
/settings/project#path_cleaning has a built-in
"test path" input that replays the full ordered chain.AiRegexHelper modal accessible
from the rule editor (Help me with Regex button) that turns natural
language into a regex. Suggest it to the user when they say "I don't know
regex" — but always validate the output against real paths via the tester.Sequential application means a generic rule placed first will swallow everything that should have hit a specific rule.
Prefer the path-cleaning-rules-update tool. It reads the current rules,
applies granular operations (append, insert, replace, remove,
reorder), auto-numbers order, and — unless you pass confirm: true —
returns a preview of the resulting rules without saving. Pass
sample_paths to see how the resulting set rewrites real paths.
First call it without confirm to get the preview, surface that to the user,
then re-send the same call with "confirm": true to save:
Because the tool does the read-modify-write for you, you don't have to fetch
the full list, renumber order, or risk clobbering existing rules — the common
failure mode when editing path_cleaning_filters directly.
When the user (or a HogQL query) opts in:
apply_path_cleaning(path_expr, team)The rules are stored once per project — they are not insight-scoped.
^-anchored either
way: replaceRegexpAll keeps scanning for further matches, so an unanchored
pattern is much more expensive than an anchored one.alias need double-escaping — ClickHouse's
replaceRegexpAll supports \0 (whole match) and \1–\9 (capture
groups). In a JSON field or SQL string literal the backslash must be
doubled, so use \\1 in path_cleaning_filters / HogQL to get the \1
backreference at the ClickHouse layer.$ — \d+ without an end anchor matches every numeric run
in any path, so /blog/2024-09-12/post becomes
/blog/<num>-<num>-<num>/post when you only meant to match the year
segment. Use \d+$ or \d+(/|$) depending on intent./ — re2 does not require it. \/ works but adds noise.(?i) at the
start of the pattern for case-insensitive matching, e.g. (?i)/users/\d+.path_cleaning_filters field is
overwrite, not append. Use path-cleaning-rules-update (which does the
read-modify-write for you) instead of hand-editing the field; if you must
edit it directly, always start from the current list.Get started with chainctlchainctl skill install public/posthog/managing-path-cleaning-rules:latest
Bring Chainguard's hardening pipeline to your first-party skills — custom rulesets, automated remediation, full audit trail.
skills.cgr.dev/public/github.com/posthog/skills/omnibus/managing-path-cleaning-rules:latest
MIT
HARDENING.md
LICENSE
SKILL.md