snowplow/referer-parser
Referer-parser for PHP identifies where your traffic comes from by parsing referer URLs. It detects search engines, social networks, email and other sources, extracting search terms and medium details so you can classify visits and attribute conversions accurately.
Architecture fit. snowplow/referer-parser is a framework-agnostic, pure-PHP library with no Laravel coupling, no service provider, no config, no persistence. In a Laravel codebase it belongs as a domain service in the ingest layer — the point where a session/visit/pageview record is created. Its contract is small and deterministic: given a Referer URL and the current page URL, it returns a classification (medium such as search / social / internal / unknown, plus source and search term). That maps cleanly onto an "attribution resolver" abstraction you can own, with the package as one replaceable implementation behind it. The $pageUrl argument matters: it is what allows the library to distinguish same-site navigation from genuine external referral, so it must be wired correctly rather than passed as null.
Integration feasibility. High. composer require snowplow/referer-parser, bind it as a singleton, call it from one or two places. The real work is not the library — it is deciding where attribution is computed and what you persist. Expect roughly a day of engineering plus a validation pass, not a sprint.
Technical risk. Low runtime risk, moderate product risk:
gclid / fbclid / msclkid click identifiers, and it cannot recover a referrer the browser never sent. With modern default referrer policies (strict-origin-when-cross-origin) and in-app browsers stripping referrers, a growing share of paid and social traffic will land as unknown. If your paid acquisition runs on click IDs, this package addresses only the organic/referral half of the problem.composer.json before legal sign-off, since Apache-2.0 requires NOTICE preservation if vendored.Key questions.
symfony/yaml (potential conflict with an existing Laravel-adjacent version pin)?unknown?Stack fit. Works across Laravel 9–11 on PHP 8.1+ subject to the package's own constraint — verify first, since this is the only hard blocker. No facade needed; register a singleton in a dedicated AttributionServiceProvider and inject it, or wrap it in your own ReferrerClassifier interface so the package never leaks into controllers and can be swapped for a click-ID-aware resolver later.
Migration path. Do not parse-and-discard. The correct sequence is:
raw_referrer (and landing_url) column to the visit/session table and start capturing the header verbatim — this is immediately valuable and reversible.medium / source / term columns.This ordering means a bad classification decision costs a re-run, not lost data — and it lets you re-derive history for free every time the upstream dataset improves.
Compatibility. The package is passive: no migrations, no published config, no global middleware. Conversely, you get none of Laravel's conventions for free — no config file for custom domains, no cache-aware dataset loading. Budget for a thin wrapper that loads custom referrer definitions from config/attribution.php and merges them with the bundled set.
Sequencing.
Maintenance. Two ongoing chores: bumping the package to pick up referrer-dataset updates (quarterly is reasonable), and keeping your own custom-domain list current. The second is the one that rots silently — an internal staging domain or a new regional subdomain will start registering as an external referrer and quietly corrupt attribution until someone notices. Put a test around the custom-domain merge.
Support. Upstream is a commercial open-source project (Snowplow) with a public issue tracker; the PHP port receives less attention than the JS implementation. Because the library is small and dependency-light, the fallback of vendoring a fork is genuinely viable — treat that as the mitigation rather than a support contract.
Scaling. Effectively free. Classification is in-memory string matching over a few thousand domains — sub-millisecond, no I/O. The only scaling hazard is construction cost: if the dataset is YAML-parsed on every instantiation and you instantiate per request, you pay that parse on every pageview. Bind it as a singleton and, for bulk re-derivation, chunk the job and reuse one parser instance across the batch. Memory footprint per worker is small and constant.
Failure modes.
| Mode | Symptom | Mitigation |
|---|---|---|
| Missing/stripped referrer | Legitimate paid + social traffic bucketed unknown, inflating "direct" |
Report unknown as its own segment; never present it as direct; pair with click-ID resolver |
| Dataset staleness | New source misclassified or unmatched | Scheduled dependency bump; alert on unexplained unknown growth |
| Internal referral leakage | Own subdomains appear as external sources | Explicit internal-domain allowlist in the wrapper; test coverage |
$pageUrl omitted |
Same-site navigation classified as external | Enforce non-null at the call site, not optional |
| Spam/bot referrers | Junk sources pollute source reports | Filter known spam domains upstream of the parser |
| Silent reclassification | Numbers shift after a package bump with no code change | Log the dataset version alongside derived values |
Ramp-up. Half a day for a mid-level engineer to be productive; the API surface is a single parse call and a small value object. The learning cost sits in the attribution semantics — teaching the team that unknown ≠ direct, and that this library answers "where did the referrer header say they came from," not "which campaign drove the conversion." Set that expectation with marketing before the first dashboard ships, or you will spend the following quarter explaining why paid social looks underreported.
How can I help you explore Laravel packages today?