A page response no longer sets the visitor cookie. Only POST /_mbuzz/session
mints, and the inline snippet in the README's "Full-page caching" section is what
calls it. Upgrading without adding that snippet stops tracking entirely — no
cookie is ever minted, so no visitor exists and every event is dropped.
The reason: a page response can be stored by a full-page cache and replayed to
every visitor. A Set-Cookie sitting in that cache hands everyone the first
visitor's id, merging unrelated people into a single journey — corruption rather
than loss, since every row still exists and is simply attributed to the wrong
person. Nothing looks missing, which is what makes it dangerous.
Verified against nginx proxy_cache in sdk_integration_tests/scenarios/page_cache_test.rb:
before this change three visitors on a cached page received one id; after it,
three. The WordPress plugin reached the same conclusion first
(CookieBootstrap::CONTEXT_PAGE).
-
Attribution behind a full-page cache. A cached page is served without entering the application, so the middleware never ran, no visitor cookie was minted, and every later event and conversion was dropped for having no visitor — silently, with no HTTP call and nothing logged. The middleware now answers
POST /_mbuzz/session, a path caches don't store, and the server sets the cookie on that response. The id is never created or read in JavaScript, so it staysHttpOnlyand keeps its full two-year life. Add the inline snippet from the README's "Full-page caching" section to your base template. -
A dropped call now says why.
event()andconversion()returned failure with no request and no log when novisitor_idand nouser_idcould be resolved — a dropped conversion and a delivered one looked identical from the caller's side. Both now log a warning naming the call, the reason, and the fix. Not behind the debug flag: the customers who hit this are the ones not running in debug. -
__version__said0.8.2while the package was0.8.3. Both now track the release.
-
Django middleware (
mbuzz.middleware.django.MbuzzMiddleware) and FastAPI/Starlette middleware (mbuzz.middleware.fastapi.MbuzzMiddleware). Both frameworks were advertised in the package metadata but only Flask middleware shipped, so those users got no cookie minting at all — a 100% silent-drop path unrelated to caching. Each includes the session endpoint above. -
The
djangoandflaskextras now actually declare their framework, instead of being empty.
identifierparameter onconversion(). Pass the email or external ID asuser_idinstead. The backend/conversionsendpoint has never permitted this field — Rails strong params strip it — and the events endpoint treatsidentifier.emailexactly asuser_id. The param still serializes into the payload (backwards-compatible — existing callers keep working) but now emits aDeprecationWarningonce per process. Will be removed in a future major release. Matched by deprecation inmbuzz-php1.2.1 andmbuzz-node.
- Removed
api_urlfrominit()parameters — the proxy URL (https://api.mbuzz.co/api/v1) is now hardcoded. This prevents accidental bypass of the edge ingest proxy. For development,config.api_urlcan still be set directly after init.
event()andconversion()now handle proxy-buffered responses gracefully — when the edge proxy accepts a request but Rails is temporarily unreachable, the SDK returnsTrackResult(success=True)with nil IDs instead ofTrackResult(success=False).
- Default API URL updated to
https://api.mbuzz.co/api/v1— traffic now routes through the edge ingest proxy for improved reliability.
identify()now writesuser_idback to context — after a successful API call,user_idis stored in theRequestContextso that subsequentconversion()calls in the same request can resolve it.
- Navigation-aware session creation — middleware now only creates server-side sessions for real page navigations, filtering out Turbo frames, htmx partials, fetch/XHR, prefetch, and other sub-requests. Uses browser-enforced
Sec-Fetch-*headers as the primary signal with a framework-specific blacklist fallback for old browsers. device_fingerprint()utility — computesSHA256(ip|user_agent)[0:32], matching the server-side fingerprint for session deduplication.- Async session creation via
POST /sessions— fire-and-forget background thread on real navigations.
- 5x visit count inflation caused by concurrent sub-requests (Turbo frames, htmx) each creating separate sessions on first page load.
- Initial release with Flask middleware, visitor cookie management, event tracking, user identification, and conversion tracking.
- Session cookie removed — server handles session resolution via device fingerprint.