Tracking transferred bytes on the client side?
Shivamyadav
Frontend Engineer · 2024-01-25
I'm tracking actual data transferred to users (videos, images, JS, CSS, fonts, etc.) and need to store usage statistics per learner/client. What's the recommended approach to avoid losing usage data when users: Refresh or close the tab Navigate away Lose internet connectivity Experience browser crashes Would periodic syncing + navigator.sendBeacon() + local storage retries be...
Accurate usage accounting for rich media requires acknowledging that the browser environment is unstable. Users close tabs, lose connectivity, or crash without warning. Building a tracking system that survives these events means embracing periodic heartbeats, durable local storage, and delivery mechanisms optimized for unload. This post provides a practical architecture and discusses how it interacts with server-side rate limiting and video-access security.
Measurement Strategy
Use the Resource Timing API and Network Information API to infer total transferred bytes rather than intercepting every request manually. Summarize per-session totals including video, images, stylesheets, scripts, and fonts. Focus on observable events such as play, seek, pause, and end for video analytics, since they correlate better with billing and engagement than raw byte counts alone.
Reliable Delivery
Combine periodic background sync with navigator.sendBeacon() during unload. sendBeacon guarantees the browser keeps the channel open briefly for POST requests, which is ideal for finalizing session totals. Retry failed deliveries by storing unsent payloads in IndexedDB or localStorage and replaying them when connectivity returns. Use exponential backoff to avoid flooding flaky networks.
Privacy and Attribution
Usage data touches privacy policy requirements, so anonymize or hash identifiers and disclose collection in your terms. Guard against inflated numbers by validating that byte totals are plausible for the content served, and reconcile discrepancies server-side with CDN logs where available.
Security and Link Sharing
If your system also protects paid content, tracking behavior alone cannot stop redistribution. Secure video embed links using short-lived signed tokens as described in How to secure an API-generated video embed link and prevent user sharing?, where the same backend that issues playback links can validate that usage records match authorized sessions.
Conclusion
Resilient client-side tracking is a reliability problem first and an analytics problem second. Use beacons for last-chance delivery, local storage for retries, and server-side reconciliation to keep usage data trustworthy.
Related Posts
- How to secure an API-generated video embed link and prevent user sharing?
- Tracking transferred bytes on the client side?
- Recently Started Paying for AI
These related posts explain API-backed delivery, client-side integrity, and AI-related data reliability.
Measuring Actual Network Usage
To track transferred bytes accurately, hook into Resource Timing API entries. The transferSize property reports the size of the fetched resource including headers, while encodedBodySize reports just the compressed payload. For images and fonts, these metrics account for actual wire bytes, not file size. Aggregate these per page load or per user session.
Edge cases: service workers may serve cached resources with zero transfer size, which is correct. Chunked transfer encoding may result in transferSize being 0 in older browsers; use nextHopProtocol and fallback heuristics if needed. Always respect privacy: do not fingerprint users through network patterns, and provide opt-out mechanisms if you're in a regulated jurisdiction.
Storing Usage Statistics Safely
Use navigator.sendBeacon to send usage data during unload events. It's non-blocking and designed for analytics. Combine with localStorage to queue events for retry when offline. Periodically flush the queue when online. On the backend, Laravel can accept these POST requests and batch-insert into a usage table. See How to secure an API-generated video embed link and prevent user sharing? for controlling access to bandwidth-intensive content.
Backend Storage and Aggregation
Storing per-resource transfer data requires a schema design that balances granularity with query performance. A single row per resource per page load can grow quickly. Consider summarizing at the session or daily level in materialized views or cache. Use Laravel's chunk-based inserts to batch incoming usage reports from many users. For analytics, pre-compute rolling sums by user, resource type, and time window.
Privacy regulations may require anonymization or deletion on request. Hash user identifiers before storing linking tables, or use tokenized user IDs that can be rotated. Provide users with a data usage dashboard that aggregates their own transfer history.
Reliability Strategies in Detail
navigator.sendBeacon sends data with minimal impact on navigation. Use it during beforeunload and visibilitychange events. Fall back to fetch with keepalive if sendBeacon is unavailable. Queue events in localStorage under a namespaced key, and attempt to flush on subsequent visits if offline. Handle quota exceeded errors gracefully by trimming old entries or switching to IndexedDB for larger queues. Use the Background Sync API to retry when connectivity returns.
See How to secure an API-generated video embed link and prevent user sharing? for controlling access to large files and Analytics dashboard for visualizing bandwidth metrics.
Sampling and Performance Considerations
Tracking every single byte for every resource on every page can generate substantial data volume. Use sampling to reduce overhead: track every Nth page load or every Nth resource per page. The Resource Timing API itself is performant, but transmitting thousands of entries per session stresses the client and backend. Aggregate on the client: send only the total transfer size per page and resource type counts, rather than a full resource list.
Consider using the Beacon API's limit: browsers truncate beacon payloads that exceed about 64KB. If your resource list is large, compress or summarize before sending. Service workers can intercept fetch events and perform counting in a centralized location, reducing the need to instrument every page independently.
Privacy, Security, and Compliance
Network usage data can be considered personal data under privacy laws if it relates to identifiable users. Disclose tracking in your privacy policy and offer opt-out. Secure the endpoint that receives usage reports with authentication and rate limiting to prevent abuse or enumeration attacks. Hash user identifiers before storing them in analytics tables if you need to join with other systems.
Related: How to secure an API-generated video embed link and prevent user sharing? for controlling content delivery, Analytics dashboard for visualizing bandwidth, and Laravel Octane benchmark comparing Swoole, OpenSwoole, RoadRunner, FrankenPHP for serving high-volume assets.