Title: DownloadSentinel
Author: Alex Hedström
Published: <strong>September 4, 2026</strong>
Last modified: September 4, 2026

---

Search plugins

![](https://ps.w.org/downloadsentinel/assets/banner-772x250.png?rev=3681359)

![](https://ps.w.org/downloadsentinel/assets/icon-256x256.png?rev=3681359)

# DownloadSentinel

 By [Alex Hedström](https://profiles.wordpress.org/alexhedstrom/)

[Download](https://downloads.wordpress.org/plugin/downloadsentinel.1.0.0.zip)

 * [Details](https://wordpress.org/plugins/downloadsentinel/#description)
 * [Reviews](https://wordpress.org/plugins/downloadsentinel/#reviews)
 *  [Installation](https://wordpress.org/plugins/downloadsentinel/#installation)
 * [Development](https://wordpress.org/plugins/downloadsentinel/#developers)

 [Support](https://wordpress.org/support/plugin/downloadsentinel/)

## Description

DownloadSentinel lets you upload files to WordPress and share special download links
that hide the real storage location. Claimed transfers update file counters, while
configurable activity logging helps you review download traffic in a clear admin
screen.

Under Settings  DownloadSentinel you get four tabs:

 * Files — upload or import from Media Library, copy secure links, edit locks, replace
   files, regenerate tokens
 * Activity — totals, 14-day chart, filters, CSV export, and recent download events
 * Settings — pretty permalinks, force download, bot filtering, tracking, proxy 
   IP trust, and uninstall cleanup
 * About — plugin info and version

How protection works:

 * Files are stored under opaque names in a protected uploads folder blocked from
   direct browser access
 * Each file gets a unique token URL (/download/{token}/ by default, or ?ds_dl=token).
   The pretty path base is configurable under Settings.
 * Optional expiry, max downloads, and password per file
 * The plugin streams the file through WordPress, or optionally offloads it to a
   configured web server—visitors never see the real path
 * Optional shortcode: [downloadsentinel token=”abc…”] (prefer token= over enumerable
   id=)
 * Dashboard widget for a quick pulse on downloads

## Installation

 1. Upload the `downloadsentinel` folder to the `/wp-content/plugins/` directory.
 2. Activate the plugin through the Plugins menu in WordPress.
 3. Open Settings  DownloadSentinel.
 4. Upload a file on the Files tab and copy the secure link onto your site.

## FAQ

### Can visitors guess the real file URL?

Files are stored under opaque random names in a protected uploads folder. Apache.
htaccess and IIS web.config deny direct browser access (rewritten on each admin 
visit if missing). Downloads are only served when a valid token is requested through
the plugin. Settings and Tools  Site Health run a live canary probe to verify that
the folder rejects direct requests. New uploads, imports, and replacements are blocked
when the folder is exposed or when protection cannot be verified.

### What about Media Library imports?

Import copies the file into protected storage and creates a token link. Delete Media
Library original after import is checked by default and required when the attachment
URL is still publicly reachable — otherwise the Media URL stays open and token locks(
password, expiry, max downloads) do not apply to it.

After deletion, DownloadSentinel verifies that the attachment record, original file,
generated image sizes, and known backup images are gone. It retries remaining filesystem
cleanup and rolls back the protected import when deletion cannot be verified.

### Can I allow ZIP or other risky types with a filter?

No. New uploads use a strict allowlist (documents, images, audio, video). Archives,
executables, scripts, SVG/HTML/XML, and similar types stay on a hard disallow list.
The downloadsentinel_allowed_mime_types filter cannot reintroduce them — those extensions
are stripped from the allow map. Older protected files that still use a blocked 
extension are refused at download time and flagged on the Files screen; delete or
replace them with an allowed type.

### What about nginx?

.htaccess is ignored on nginx. Add a deny rule for the protected folder (opaque 
filenames alone are not enough if the directory is publicly readable):

location ^~ /wp-content/uploads/downloadsentinel/ {
 deny all; }

Then reload nginx. Without this rule, nginx may serve files directly and bypass 
password, expiry, and download limits.

When nginx is detected, or when a live probe finds the folder exposed or cannot 
verify protection, DownloadSentinel shows guidance on its settings screen and under
Tools  Site Health. Writes remain blocked until the probe confirms that direct access
is denied.

### How should I embed a download link?

Prefer the token shortcode shown on each file card: [downloadsentinel token=”…”].
Do not use id= in public content — numeric IDs are enumerable. Resolving by id= 
requires an administrator capability by default and never renders files that have
no password.

### Behind a reverse proxy?

By default the plugin uses REMOTE_ADDR for password lockouts and activity hashing.
If your host sits behind Cloudflare or another proxy that overwrites client IP headers,
enable Trust proxy IP headers in Settings. Do not enable it on a publicly reachable
origin that does not sanitize X-Forwarded-For.

### How are password attempts limited?

Password submissions are limited to 10 attempts per download token and client IP,
followed by a 15-minute lockout. Attempt reservations are serialized before password
verification so concurrent requests cannot exceed the limit. If synchronization 
is temporarily unavailable, verification fails closed for a few seconds instead 
of allowing an uncounted attempt.

After a correct password, DownloadSentinel issues a signed, HTTP-only authorization
cookie for that token and password. The cookie lasts for one hour and is automatically
invalidated when the file password changes.

### What download activity data is stored?

Depending on the tracking settings, activity records can contain the download time,
file ID, a salted hash of the client IP address, WordPress user ID, user agent, 
and referrer URL. Raw IP addresses are not stored. Bot activity and logged-in-user
activity can be excluded from the activity log; file counters still count every 
claimed transfer.

Activity logs can be exported or cleared from the Activity tab. By default, uninstall
preserves protected files and database tables, including activity records. Enable
Delete files on uninstall before uninstalling if you want the plugin to purge its
stored files and tables.

### Will pretty links work without permalinks?

If WordPress permalinks are plain, or pretty permalinks are disabled in Settings,
links fall back to a query-string format that still works.

The pretty path base defaults to /download/{token}/. Change Pretty link base under
Settings  DownloadSentinel if that slug conflicts with a page or another plugin.

After deactivating or uninstalling, rewrite rules are flushed automatically. If 
an old pretty URL still resolves oddly, open Settings  Permalinks and click Save
Changes once.

### Does uninstall delete my files?

Only if you enable Delete files on uninstall in Settings. By default, uninstall 
removes plugin settings only — protected files and database tables stay on the server
until you turn that option on or delete them manually.

### What happens if deleting a protected file fails?

Deletion first disables token access, then removes the stored bytes and asks the
database layer to delete the record and associated activity logs. Success is shown
only after the file and record removals report success. If filesystem or database
cleanup fails, the retained record stays disabled so an administrator can correct
the underlying problem and safely retry deletion.

### Does it work on WordPress Multisite?

Yes. Network-activating sets up every site (tables, protected storage, rewrites).
New sites created while the plugin is network-active are set up automatically. Uninstall
and network deactivation also walk all sites.

### How do max download limits work?

Each successful GET that starts streaming the file claims one slot before the body
is sent, so concurrent requests cannot oversell the limit. Expiry is also enforced
in that atomic claim. If the server cannot open or read the file, that slot is released.
Client disconnects after transfer begins still count — otherwise visitors could 
abort and retry to bypass the cap. Long transfers call set_time_limit(0) when allowed
so PHP max_execution_time is less likely to cut off mid-stream after a claim.

DownloadSentinel supports single HTTP byte ranges, so browsers and download clients
can resume interrupted transfers. Each range request is a separate claimed GET and
therefore counts toward a file’s max-download limit.

### Can large downloads be served without tying up PHP workers?

Yes, when the web server is configured for internal file offloading. Developers 
can use the `downloadsentinel_offload_response` filter to return an array with `
header` and `value` keys. The only accepted headers are `X-Sendfile` and `X-Accel-
Redirect`.

For Apache mod_xsendfile, the value is normally the authorized absolute path:

    ```
    add_filter(
        'downloadsentinel_offload_response',
        function ($offload, $file, $path) {
            return array(
                'header' => 'X-Sendfile',
                'value'  => $path,
            );
        },
        10,
        3
    );
    ```

For nginx, the value must be an internal URI backed by a correctly configured `internal`
location and `alias`:

    ```
    add_filter(
        'downloadsentinel_offload_response',
        function ($offload, $file, $path) {
            return array(
                'header' => 'X-Accel-Redirect',
                'value'  => '/_downloadsentinel/' . rawurlencode(basename($path)),
            );
        },
        10,
        3
    );
    ```

Do not enable either response until the corresponding server module or internal 
location is configured and tested. An unhandled X-Sendfile header can disclose the
filesystem path without serving the file.

Without server offloading, the plugin uses bounded 1 MiB chunks (filterable with`
downloadsentinel_stream_chunk_size`) and sends only the requested range. Chunking
limits PHP memory use, but the PHP worker remains occupied for the duration of the
transfer.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“DownloadSentinel” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ Alex Hedström ](https://profiles.wordpress.org/alexhedstrom/)

[Translate “DownloadSentinel” into your language.](https://translate.wordpress.org/projects/wp-plugins/downloadsentinel)

### Interested in development?

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

## Changelog

#### 1.0.0

 * Initial release.

## Meta

 *  Version **1.0.0**
 *  Last updated **13 hours ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.3 or higher **
 *  Tested up to **7.1**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [download](https://wordpress.org/plugins/tags/download/)[download links](https://wordpress.org/plugins/tags/download-links/)
   [file protection](https://wordpress.org/plugins/tags/file-protection/)[secure download](https://wordpress.org/plugins/tags/secure-download/)
 *  [Advanced View](https://wordpress.org/plugins/downloadsentinel/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/downloadsentinel/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/downloadsentinel/reviews/)

## Contributors

 *   [ Alex Hedström ](https://profiles.wordpress.org/alexhedstrom/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/downloadsentinel/)