Title: Bouzin Download Stats Manager
Author: bouzin
Published: <strong>July 21, 2026</strong>
Last modified: July 21, 2026

---

Search plugins

![](https://ps.w.org/bouzin-download-stats-manager/assets/icon-256x256.png?rev=3617195)

# Bouzin Download Stats Manager

 By [bouzin](https://profiles.wordpress.org/bouzin/)

[Download](https://downloads.wordpress.org/plugin/bouzin-download-stats-manager.1.8.7.zip)

 * [Details](https://wordpress.org/plugins/bouzin-download-stats-manager/#description)
 * [Reviews](https://wordpress.org/plugins/bouzin-download-stats-manager/#reviews)
 *  [Installation](https://wordpress.org/plugins/bouzin-download-stats-manager/#installation)
 * [Development](https://wordpress.org/plugins/bouzin-download-stats-manager/#developers)

 [Support](https://wordpress.org/support/plugin/bouzin-download-stats-manager/)

## Description

Bouzin Download Stats Manager counts downloads of files already stored in a chosen
folder of a WordPress installation.

It is intended for sites that already contain many direct links to PDF, EPUB, MOBI,
ZIP, or other downloadable files. The plugin rewrites matching front-end links to
a secure local download endpoint, so existing links do not need to be edited manually.

Main features:

 * Choose a folder relative to the WordPress installation root.
 * Safely create a missing custom folder when settings are saved.
 * Optionally monitor the native WordPress Media Library uploads folder and manage
   files with the standard Media screen.
 * Choose the file extensions to track.
 * Support files stored in subfolders.
 * Rewrite matching links without modifying post content in the database.
 * Validate every requested path against the configured folder and allowed extensions.
 * Block directory traversal and symbolic-link escapes with canonical path checks.
 * Store new-installation download events directly in indexed, per-site WordPress
   database tables.
 * Preserve a controlled, verified CSV migration only for sites upgrading with historical
   statistics.
 * Optionally store only a server-provided two-letter country code and a broad Computer/
   Mobile category.
 * Display sortable and paginated download statistics in WordPress administration.
 * Allow every logged-in WordPress role to view statistics while keeping settings
   and destructive actions administrator-only.
 * Show an at-a-glance dashboard with the total, downloaded files, top three files,
   three latest downloads, and optional detected country/device rankings.
 * Display an accessible, dependency-free SVG download-evolution chart with automatic
   daily, weekly, monthly, or yearly grouping.
 * Display the last download date and time for each file in the WordPress timezone,
   with its optional country and device metadata on the same line group.
 * Search and filter statistics by period, exact detected country, and broad device
   category.
 * Choose among disabled, standard, enhanced, and strict bot-filter levels without
   blocking downloads.
 * Request strict-mode click proofs at download time so cached pages do not contain
   expiring tokens.
 * Stream a real CSV export from either storage mode and reset all active statistics.
 * Filter database statistics by the existing periods or by an available calendar
   year.
 * Optionally remove all plugin data during uninstallation.
 * Avoid external services, remote scripts, telemetry, and third-party tracking.

#### Privacy

By default, the plugin records only:

 * the local WordPress date and time of the download;
 * the relative path of the downloaded file.

If an administrator explicitly enables the optional classifications, new rows may
also contain:

 * a validated two-letter country code already supplied locally by the server or
   CDN;
 * `desktop`, `mobile`, or `unknown`.

It does not record IP addresses, full user-agent strings, account identifiers, or
cookies and does not send visitor data to external services. Request headers may
be examined temporarily for bot detection and optional classification but are not
persisted. Strict mode requests a short-lived signed token from the local WordPress
REST API after a trusted click; the token contains no personal data and the response
is not cached. When upgrading from an older version, legacy log rows remain readable
and legacy privacy migration removes previously stored IP addresses and user-agent
strings.

New installations store events directly in two indexed tables that use the site’s
WordPress table prefix. Sites upgrading from a legacy CSV version keep the journal
intact until an administrator starts a small-batch, resumable migration and its 
normalized rows are verified in source order. New events are not duplicated in the
retained CSV after cutover. The export action always generates a downloadable CSV
and is protected by an administrator capability check and nonce.

### Credits

 * Denis PIERREHUMBERT
 * Adrien Bouzin
 * Aurélien Bouzin
 * ChatGPT

## Installation

 1. Upload the plugin ZIP through “Plugins > Add New > Upload Plugin”, or copy the 
    plugin folder to `/wp-content/plugins/`.
 2. Activate “Bouzin Download Stats Manager”.
 3. Open “Downloads > Settings” in WordPress administration.
 4. Choose the WordPress Media Library mode, or enter a custom folder relative to the
    WordPress installation root, for example `ebooks` or `download`.
 5. Enter the allowed extensions as a comma-separated list, for example `pdf, epub,
    mobi, zip`.
 6. Save the settings.

Existing front-end links that point to matching files in the configured folder are
rewritten automatically when HTML pages are rendered.

If a custom folder does not exist, the plugin tries to create it safely when settings
are saved. It never moves existing files. In Media Library mode, use the standard
WordPress “Media” screen to upload and manage files.

## FAQ

### Do I have to change existing links?

No. Matching anchor links are rewritten dynamically on front-end HTML responses.
The stored post, page, widget, or menu content is not modified in the database.

### How can I create a folder or upload files without FTP?

The settings page offers a simple WordPress Media Library mode and a button that
opens the native Media screen in a new tab. This mode monitors the real WordPress
uploads directory and its year/month subfolders. For a custom relative folder, the
plugin tries to create the missing directory when settings are saved. It does not
include an arbitrary server file manager and never moves existing files.

### Can a visitor request another server file by changing the URL parameter?

No. The download handler rejects invalid paths, `.` and `..` segments, files outside
the configured folder, disallowed extensions, directories, unreadable files, and
symbolic links that resolve outside the monitored folder.

### Which file types can be tracked?

Document and archive extensions made of 1 to 16 alphanumeric characters can be configured.
Executable, script, server-configuration, key, and database extensions are always
rejected, even if entered in the settings.

### Where are the statistics stored?

New installations store statistics directly in the per-site tables `{prefix}fdsb_files`
and `{prefix}fdsb_download_events`. The prefix is obtained from WordPress and is
not assumed to be `wp_`.

Sites upgrading from a legacy CSV version may still have the historical journal 
in:

    ```
    wp-content/uploads/bouzin-download-stats-manager/journal.csv
    ```

The actual uploads base directory is obtained from WordPress, so custom uploads 
paths and multisite installations are supported. The controlled migration is displayed
only when this legacy storage remains active.

### Can the database migration lose or duplicate statistics?

This applies only to sites upgrading with historical CSV data. The CSV stays active
during import. Each imported row is identified by its original byte offset, making
retries idempotent. Downloads received while migration is running are written to
both stores under the CSV lock. The plugin then compares the event count and a progressive
SHA-256 checksum before it changes the active storage mode. If any database write
or verification fails, the switch is refused and CSV mode remains active. The historical
CSV is not moved or deleted by migration.

### Can I still download a CSV after migration?

Yes. The administrator export action streams the database in bounded batches through`
fputcsv()` and sends it with CSV attachment headers. It does not display the export
as an HTML or text page.

### Does the plugin collect personal data?

The plugin does not store IP addresses, full user-agent strings, account identifiers,
or cookies. By default, the journal contains only the download date/time and relative
file path. If an administrator enables the optional settings, the journal may additionally
contain a server-provided two-letter country code and the broad category `desktop`,`
mobile`, or `unknown`. Headers are processed only for the current request and are
not saved.

### How are country and device information detected?

Country collection is disabled by default. When enabled, the plugin reads only a
validated two-letter code already supplied locally by a supported server or CDN 
value. Supported sources include Cloudflare, CloudFront, Vercel, 20i, WP Engine 
GeoTarget, Kinsta/WordPress VIP GeoIP variables, Fastly origin headers, bunny.net,
Akamai EdgeScape, and common local GeoIP server variables. It never performs a remote
geolocation request and never stores the visitor IP. If the host or CDN does not
expose a local country value, the country remains unknown. Developers can integrate
another trusted local source with the `fdsb_download_country_code` filter.

Device collection is also disabled by default. When enabled, the plugin prefers 
a low-detail mobile client hint or a server-provided CloudFront device category,
then temporarily examines the user-agent string as a fallback. Only `desktop`, `
mobile`, or `unknown` is stored. Detection is approximate and can be affected by
browser privacy settings, VPNs, or proxy services.

### Are prefetch, bot, and crawler requests counted?

Known browser and proxy prefetch headers are always excluded from the journal. Standard
mode filters known bot signatures. Enhanced mode, enabled by default, also detects
automation clients and suspicious browser-header combinations. Strict mode requests
a fresh, short-lived signed token from the local WordPress server after a trusted
JavaScript click. The request uses no cookie or personal identifier and is explicitly
excluded from caches. A direct link or browser without JavaScript still receives
the file but is not counted in strict mode. After first enabling strict mode, purge
any existing full-page or CDN cache once so pages include the helper script. No 
method can identify every automated request, including bots that fully control a
real browser.

### What happens on uninstallation?

Data is kept by default. Enable “Delete options, tables, and journals during uninstallation”
before deleting the plugin if you want its options, per-site database tables, and
journal files removed.

### Does the plugin work on Nginx?

The plugin itself works on Nginx. Apache `.htaccess` and IIS `web.config` protection
files do not control Nginx, so administrators with sensitive legacy logs should 
also deny direct access to the journal directory in their Nginx configuration. Version
1.6.0 no longer stores IP addresses or user-agent strings.

## Reviews

![](https://secure.gravatar.com/avatar/0de8ed31a0e44f2a32740ef7803962224da25536c0a8aac840abe39a2e34452e?
s=60&d=retro&r=g)

### 󠀁[SUPER GENIAL](https://wordpress.org/support/topic/super-genial-2/)󠁿

 [galak1973](https://profiles.wordpress.org/galak1973/) July 21, 2026

SUPER GENIAL JE LE RECOMMANDE

 [ Read all 1 review ](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/)

## Contributors & Developers

“Bouzin Download Stats Manager” is open source software. The following people have
contributed to this plugin.

Contributors

 *   [ bouzin ](https://profiles.wordpress.org/bouzin/)

[Translate “Bouzin Download Stats Manager” into your language.](https://translate.wordpress.org/projects/wp-plugins/bouzin-download-stats-manager)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/bouzin-download-stats-manager/),
check out the [SVN repository](https://plugins.svn.wordpress.org/bouzin-download-stats-manager/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/bouzin-download-stats-manager/)
by [RSS](https://plugins.trac.wordpress.org/log/bouzin-download-stats-manager/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.8.7

 * Removed the redundant Plugin URI so it does not duplicate the Author URI.

#### 1.8.6

 * Updated the declared Plugin URI and Author URI to `https://bouzin.ch/`.

#### 1.8.5

 * Reorganized the complete statistics summary into a balanced three-column desktop
   grid when country and device data are available.
 * Placed Downloads, Downloaded files, and Top countries on the first row, followed
   by Top downloaded files, Latest downloads, and Most used devices on the second
   row.
 * Kept optional country and device cards hidden when no corresponding data is detected,
   with the existing responsive single-column mobile layout preserved.
 * Preserved the SVG chart, filters, sorting, pagination, CSV export, database and
   legacy CSV compatibility, anti-bot modes, public downloads, privacy, and all 
   stored statistics.

#### 1.8.4

 * Renamed the distinct-file card to “Downloaded files” and clarified that it counts
   different files in the current selection.
 * Added a local, responsive and accessible SVG download-evolution chart without
   any remote service or third-party chart library.
 * Automatically groups chart values by day for one month, by week for three or 
   six months, by month for one year or a selected calendar year, and by year for
   all dates.
 * Hides the chart, top-files card, and latest-downloads card when the current selection
   contains no data.
 * Replaced the visible PC label with the broader Computer label while preserving
   the existing internal `desktop` value and all stored statistics.
 * Preserved filters, sorting, pagination, database and legacy CSV compatibility,
   CSV export, country/device detection, anti-bot modes, public downloads, and privacy
   behavior.

#### 1.8.3

 * Expanded local country detection for documented WP Engine GeoTarget, Kinsta/WordPress
   VIP, Fastly, bunny.net, and Akamai EdgeScape values.
 * Added PHP environment-variable fallback for hosts that expose GeoIP values through
   PHP-FPM rather than the `$_SERVER` array.
 * Kept country collection optional and local: no IP address is stored, no remote
   lookup is performed, and an unavailable country remains unknown.
 * Preserved Cloudflare, CloudFront, Vercel, 20i, custom-filter, privacy, database,
   CSV, download, and anti-bot behavior.

#### 1.8.2

 * Made the administrator capability and nonce checks explicit inside the reset 
   handler before reading the confirmation phrase.
 * Preserved the mandatory phrase, final confirmation dialog, and all existing functionality
   while making the protection directly recognizable to static Plugin Check analysis.

#### 1.8.1

 * Added a mandatory exact confirmation phrase before the final statistics-reset
   confirmation dialog.
 * Enforced the same phrase server-side after the administrator capability and nonce
   checks, so JavaScript cannot bypass the protection.
 * Preserved all statistics, database, legacy migration, CSV export, access-control,
   download, privacy, and anti-bot behavior.

#### 1.8.0

 * Made the statistics page available to every logged-in WordPress role through 
   the standard `read` capability.
 * Kept the settings page, CSV export, reset action, migration controls, and all
   data-changing actions restricted to administrators with `manage_options` and 
   nonces.
 * Strengthened the statistics-reset confirmation by checking every form submission,
   including keyboard submission.
 * Made fresh installations use the indexed WordPress database tables immediately,
   without creating an empty legacy CSV or displaying a migration request.
 * Retained the verified CSV migration only for existing installations so historical
   statistics remain fully compatible and are never discarded.
 * Simplified the active database message while preserving all downloads, filters,
   bot modes, privacy choices, file-location modes, exports, and uninstall behavior.

#### 1.7.9

 * Added safe automatic creation of a missing custom monitored folder on first activation
   or when settings are saved.
 * Added an optional WordPress Media Library mode that uses the native uploads directory
   and opens the standard Media screen without moving files.
 * Refused automatic creation inside WordPress core, plugin, theme, and private 
   statistics directories, and reports host permission failures without changing
   the saved folder.
 * Explicitly blocked current and legacy private statistics directories from the
   public download endpoint, including when the entire uploads directory and CSV
   files are monitored.
 * Preserved the existing folder option, extensions, links, statistics, database
   migration, CSV export, filters, bot modes, privacy choices, translations, and
   uninstall behavior.

#### 1.7.8

 * Aligned the main class with the existing four-character `FDSB_` prefix already
   used by the plugin’s options, hooks, actions, constants, and assets.
 * Updated the internal bootstrap and activation callback to use `FDSB_Download_Stats_Manager`.
 * Kept every feature, option, table, public URL, stored statistic, export, migration,
   filter, bot mode, privacy choice, translation, and design unchanged.

#### 1.7.7

 * Renamed the declared main class with the explicit `Bouzin_` plugin prefix for
   the WordPress.org global naming-convention check.
 * Updated the internal bootstrap and activation callback to use the same prefixed
   class name.
 * Kept all features, options, tables, public URLs, stored statistics, exports, 
   migration, filters, bot modes, privacy choices, translations, and design unchanged.

#### 1.7.6

 * Added explicit WordPress sanitization at the two new read-only country and device
   GET boundaries so Plugin Check can verify the data flow directly.
 * Kept filter behavior, statistics, options, database tables, public URLs, stored
   data, exports, migration, bot modes, privacy choices, design, and translations
   unchanged.

#### 1.7.5

 * Added exact country and device filters based on individual download events in
   both CSV and database storage modes.
 * Added top detected-country and device summary cards that remain hidden when no
   corresponding metadata exists.
 * Moved the optional latest country and device details below “Last download” and
   removed the two misleading aggregate table columns.
 * Modernized the administration layout with responsive cards, panels, filters, 
   tables, accessible focus styles, and reduced-motion support.
 * Simplified the active database-storage message while retaining the historical
   CSV safety copy and the real CSV export action.
 * Preserved all existing options, database schemas, public and legacy download 
   URLs, stored statistics, migration behavior, bot modes, privacy choices, and 
   credits.

#### 1.7.4

 * Removed the discouraged manual `load_plugin_textdomain()` call and relies on 
   WordPress automatic just-in-time translation loading.
 * Kept the literal text domain, English source strings, translation template, and
   complete French translation catalogs unchanged.
 * Kept all plugin behavior, options, database tables, URLs, stored statistics, 
   privacy settings, and download handling unchanged.

#### 1.7.3

 * Changed all translatable source strings to English for WordPress.org internationalization.
 * Added complete bundled French translations for `fr_FR` and `fr_CH`, plus the 
   translation template.
 * Kept all options, database tables, public URLs, stored statistics, migration 
   behavior, privacy choices, and download behavior unchanged.

#### 1.7.2

 * Documents every necessary direct query to the plugin-owned statistics tables 
   with narrowly scoped Plugin Check/PHPCS annotations.
 * Keeps all database statements, table schemas, storage options, public URLs, migration
   behavior, statistics, privacy choices, and download behavior unchanged.

#### 1.7.1

 * Added the 20i CDN `X-Country` request header as a validated local country source.
 * Kept all existing hosting-provider sources unchanged and gave the 20i fallback
   the lowest priority.
 * Continued to store only the sanitized two-letter country code, without storing
   an IP address or calling an external API.

#### 1.7.0

 * Added optional, administrator-started migration from the historical CSV to two
   indexed WordPress database tables.
 * Added resumable 5,000-line import batches, idempotent source offsets, simultaneous-
   download dual writes, and a progressive integrity checksum before cutover.
 * Kept CSV mode active automatically whenever migration, table creation, a dual
   write, or verification fails.
 * Preserved the original CSV as a safety copy and stopped extending it after a 
   successful database cutover.
 * Changed CSV export to stream database events in bounded batches after migration
   while preserving attachment headers and `fputcsv()` output.
 * Added calendar-year choices to the existing period selector when database years
   are available.
 * Preserved all existing download URLs, link rewriting, bot modes, privacy options,
   country/device display, summary cards, search, sorting, pagination, reset, and
   credits.
 * Added cleanup of the per-site plugin tables only when the existing clean-uninstall
   option is enabled.

#### 1.6.5

 * Replaced strict-mode tokens embedded in cacheable HTML with short-lived tokens
   requested from a local, non-cacheable WordPress REST endpoint after a trusted
   click.
 * Added optional, disabled-by-default country and Computer/Mobile collection without
   storing IP addresses or full user-agent strings.
 * Added country and device columns for each file’s latest download and included
   the optional values in recent downloads and CSV exports.
 * Added automatic local detection for supported CDN/server country and device headers
   without any external geolocation request.
 * Preserved two-column journals, legacy tab-separated rows, current URLs, existing
   option names, downloads, filters, sorting, pagination, and anti-bot modes.

#### 1.6.4

 * Expanded the statistics summary with the total, distinct file count, top three
   files, and three latest download events for the current filters.
 * Kept recent-event detection to three entries while streaming the journal, avoiding
   memory growth on large histories.
 * Preserved the detailed table, sorting, search, period selection, pagination, 
   CSV export, and bot-filter settings.

#### 1.6.3

 * Added disabled, standard, enhanced, and strict bot-filter levels.
 * Added enhanced detection for automation clients and suspicious request-header
   combinations without storing headers, IP addresses, or cookies.
 * Added a short-lived signed strict-mode token that is attached only during a trusted
   JavaScript click.
 * Kept public and legacy download URLs nonce-free and fully downloadable when a
   request is excluded from statistics.
 * Preserved the 1.6.2 boolean bot-filter option for backward compatibility.

#### 1.6.2

 * Added the configurable bot and crawler filter while keeping ignored files downloadable.
 * Added the “Last download” date and time column using the WordPress timezone.
 * Restored the exact credits block, including ChatGPT, and the “Open” link label.
 * Changed the plugin slug, main filename, and text domain to `bouzin-download-stats-
   manager`.
 * Stored new journal data in `uploads/bouzin-download-stats-manager/` and safely
   copied the 1.6.1 journal without deleting the legacy file.
 * Added an explicit shutdown closure for the front-end link-rewriting output buffer.

#### 1.6.1

 * Quality: added explicit direct nonce verification before processing the settings
   form so static security checks can verify the protection.
 * Security: sanitize the public download query value before canonical path validation.
 * Standards: prefixed multisite uninstall variables to comply with WordPress naming
   conventions.

#### 1.6.0

 * Security: fixed an arbitrary local file download/path traversal vulnerability
   in the public download endpoint.
 * Security: restricted downloads to the configured folder, allowed non-executable
   extensions, regular readable files, and canonical paths inside the monitored 
   directory.
 * Security: added administrator capability checks and nonce-protected `admin-post.
   php` actions for saving settings, resetting statistics, and exporting the journal.
 * Security: added safe response headers and protected the journal directory with
   Apache/IIS deny files and an empty index file.
 * Privacy: stopped logging IP addresses and user-agent strings.
 * Privacy: added an automatic migration that removes IP addresses and user-agent
   strings from legacy log rows.
 * Privacy: added suggested text for the WordPress privacy policy guide.
 * Fixed: corrected the cleanup option mismatch that prevented uninstall data removal.
 * Fixed: corrected URL encoding in rewritten download links.
 * Fixed: added the missing configurable default page-size option.
 * Compatibility: use the WordPress uploads API instead of a hard-coded `wp-content/
   uploads` path.
 * Compatibility: use `WP_HTML_Tag_Processor` to rewrite only real anchor tags.
 * Performance: read the journal line by line instead of loading the complete file
   into memory.
 * Performance: stream large downloads in chunks.
 * Quality: added translatable interface strings, stricter validation, escaped output,
   and scoped local assets.

#### 1.5.0

 * Added filename-only display in the statistics table.
 * Added instant search on the statistics page.
 * Improved alphabetical sorting and support for subfolders.

#### 1.4.0

 * Added pagination, journal export, statistics reset, and configurable rows per
   page.

#### 1.3.7

 * Added optional data cleanup during uninstallation.

#### 1.3.0

 * Added automatic front-end link rewriting and a tracked download endpoint.

#### 1.0.0

 * Initial internal version.

## Meta

 *  Version **1.8.7**
 *  Last updated **3 days ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.2 or higher **
 *  Tested up to **7.0.2**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [analytics](https://wordpress.org/plugins/tags/analytics/)[downloads](https://wordpress.org/plugins/tags/downloads/)
   [files](https://wordpress.org/plugins/tags/files/)[logging](https://wordpress.org/plugins/tags/logging/)
   [statistics](https://wordpress.org/plugins/tags/statistics/)
 *  [Advanced View](https://wordpress.org/plugins/bouzin-download-stats-manager/advanced/)

## Ratings

 5 out of 5 stars.

 *  [  1 5-star review     ](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/?filter=5)
 *  [  0 4-star reviews     ](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/?filter=4)
 *  [  0 3-star reviews     ](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/?filter=3)
 *  [  0 2-star reviews     ](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/?filter=2)
 *  [  0 1-star reviews     ](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/?filter=1)

[Your review](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/bouzin-download-stats-manager/reviews/)

## Contributors

 *   [ bouzin ](https://profiles.wordpress.org/bouzin/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/bouzin-download-stats-manager/)