How to Track File Downloads with Privacy-Friendly Analytics
Step-by-step guide to tracking PDF, ZIP, and other file downloads using cookieless analytics. HTML snippet, JavaScript handler, gotchas, and FAQs.
TL;DR
- 1.File downloads are first-class conversion events for SaaS, ebooks, and developer tools — track them with the same delegated-listener pattern as outbound clicks.
- 2.Detect downloads by file extension on the link `href` — `.pdf`, `.zip`, `.csv`, `.dmg`, etc.
- 3.Send the filename and (optionally) the file size; do not send personal data.
- 4.For server-rendered downloads (signed URLs, force-download endpoints) you may need a server-side event instead.
- 5.In Sleek, downloads show up as a `file_download` event you can filter by filename and group by source page.
Why download tracking matters
A file download is almost always a higher-intent action than a pageview. Someone is willing to take a thing off your site and onto their machine. Whether that file is a lead magnet, a release binary, or a CSV export, the download is a real conversion you should be measuring.
For B2B marketers, downloads are the primary way to score a lead before a signup. For developer tools, they tell you which platform your installer audience is actually on. For content sites, they tell you which guides are valuable enough that readers want them offline.
This guide shows you how to set it up in a privacy-friendly way — no cookies, no consent banner, no PII — using Sleek's event API.
Step 1: install the tracking snippet
If Sleek is already installed on your site, skip this. Otherwise, paste the snippet into your `<head>`:
<script async src="https://getsleek.io/v1.js" data-site="YOUR_SITE_KEY"></script>Step 2: define which extensions count as downloads
A "download" in analytics terms is any link to a file the user takes off your site. The simplest detection method is by extension — you keep a list of file types that count, and the listener fires when a click goes to a URL ending in one of them.
A reasonable default list: `pdf`, `zip`, `tar`, `gz`, `dmg`, `pkg`, `exe`, `msi`, `csv`, `xlsx`, `docx`, `pptx`, `mp3`, `mp4`, `mov`. Add to this list as your team adds new file types — for example, you might add `epub` if you publish books.
Step 3: write the click handler
Same delegated-listener pattern as outbound clicks. One handler on `document`, one extension check, one tracking call.
const FILE_EXTENSIONS = [
'pdf', 'zip', 'tar', 'gz', 'dmg', 'pkg', 'exe', 'msi',
'csv', 'xlsx', 'docx', 'pptx',
'mp3', 'mp4', 'mov', 'wav',
]
function getExtension(href) {
try {
const url = new URL(href, window.location.href)
const match = url.pathname.match(/\.([a-z0-9]+)$/i)
return match ? match[1].toLowerCase() : null
} catch (e) {
return null
}
}
function handleDownload(event) {
const link = event.target.closest('a')
if (!link || !link.href) return
const ext = getExtension(link.href)
if (!ext || !FILE_EXTENSIONS.includes(ext)) return
const filename = link.href.split('/').pop().split('?')[0]
window.sleek('track', 'file_download', {
filename,
extension: ext,
source_page: window.location.pathname,
})
}
document.addEventListener('click', handleDownload)
document.addEventListener('auxclick', handleDownload)Step 4: handle the `download` attribute
Some links use the HTML `download` attribute to force a save dialog instead of opening the file in the browser. These behave the same way at the click level — your listener still fires — but the URL might not end in the actual filename. For example, `<a href="/api/report" download="Q4-Revenue.pdf">` triggers a download but the `href` is `/api/report`.
For this case, prefer the `download` attribute when it is present:
// Inside handleDownload, before the extension check:
const downloadAttr = link.getAttribute('download')
if (downloadAttr) {
window.sleek('track', 'file_download', {
filename: downloadAttr,
source_page: window.location.pathname,
})
return
}Step 5: server-side downloads
If your downloads are served by a backend route — a signed S3 URL generator, a "gated" download behind an email, or a Stripe-protected file — your client-side listener will only see the click on the trigger button. The actual download happens server-to-browser without ever loading a page you control.
For these you can either fire the event on click (assume the download will succeed) or fire it server-side after the file is delivered. The server-side version is more accurate. Sleek has an HTTP events API for exactly this case:
curl -X POST https://api.getsleek.io/v1/event \
-H "Content-Type: application/json" \
-d '{
"site": "YOUR_SITE_KEY",
"name": "file_download",
"url": "/downloads/whitepaper",
"props": {
"filename": "2026-state-of-analytics.pdf",
"size_bytes": 2840193,
"source": "server"
}
}'Step 6: verify and report
Open Sleek's real-time view in one tab, then trigger a download in another. You should see the `file_download` event within a couple seconds with the right filename. If you do not, double-check that your extension list includes the file type you tested with.
In your dashboard, the most useful default report is downloads grouped by `filename` over the last 30 days. That tells you which assets are pulling weight. The second most useful is downloads grouped by `source_page` — that tells you which pages are converting visitors into downloaders.
Pitfalls to avoid
- Do not include the user's email or signup form data in download events — keep them anonymous.
- Do not double-count: if you have a "download" button that triggers a JS download via Blob, do not also have a server event firing for the same action.
- Do not forget large files: a 200 MB binary download will succeed even when the user navigates away, but only if you used `sendBeacon`. Sleek's SDK does this for you.
- Do not put query strings in the filename. Strip everything after `?` before sending.
- Do not track downloads of generic, repetitive assets (favicons, fonts) — restrict your extension list to genuine downloads.
Frequently asked questions
How do I track gated downloads behind an email form?
Track two events: `lead_capture` when the form is submitted, and `file_download` when the file is actually delivered (ideally server-side after the email is sent). The first measures form conversion; the second measures whether the lead actually got their content. The gap between them is your delivery health.
Will Sleek's download tracking work for files hosted on S3 or a CDN?
Yes. The script does not care where the file is served from — it cares about the click. As long as the download link is rendered on a page where the Sleek snippet is loaded, the click is tracked. If you want to measure successful delivery (not just clicks), add a server-side event after the file is sent.
How is this different from Google Analytics enhanced measurement?
GA4 enhanced measurement detects downloads automatically by extension, which is convenient but not customizable. With the script in this guide, you control the extension list, the event name, and the properties — including server-side delivery confirmation, which GA4 cannot do without separate setup.
Can I track downloads without consent in the EU?
Yes, with Sleek. Download events do not include cookies or personal data, so they fall outside the scope of cookie consent. With Google Analytics, you cannot track downloads without consent because GA4 sets cookies regardless of the event.
Why is my download count higher than my form submission count?
Usually because some users download a file multiple times (laptop and desktop, or after refilling a form they accidentally closed). It can also mean someone shared the direct download URL — in which case the second download has no associated form submission. If the gap is large, audit whether your "gated" file URL is actually gated.
How do I report on downloads per blog post?
Send `source_page: window.location.pathname` in your event properties (the snippet above does this). Then in the dashboard, group downloads by `source_page` to see which posts drive the most asset downloads.
Track your own growth loop
Sleek Analytics gives you visitors, sources, pages, devices, and real-time behavior with one lightweight script. No cookies, no GDPR banners.
Related reading
How to Track Outbound Link Clicks (2026 Guide)
A practical 2026 guide to tracking outbound link clicks with privacy-friendly analytics. Copy-pasteable HTML and JavaScript, common pitfalls, and FAQs.
How-toHow to Track Form Submissions and Lead Conversions
Step-by-step guide to tracking form submissions and lead conversions with privacy-friendly analytics. HTML snippet, JavaScript handler, validation, and FAQs.
How-toHow to Track Scroll Depth Without Cookies
A privacy-friendly guide to tracking scroll depth on your articles and landing pages. HTML snippet, throttled JavaScript, and a workflow for using the data.
ComparisonsSleek vs Google Analytics (2026): Which Is Better for Modern Teams?
Sleek Analytics vs Google Analytics in 2026: side-by-side on setup speed, dashboard clarity, privacy, pricing, and migration. Honest take on when each tool wins.