yannkost / craft-asset-cleaner
Identify and clean up unused assets in Craft CMS
Package info
github.com/yannkost/asset-cleaner
Type:craft-plugin
pkg:composer/yannkost/craft-asset-cleaner
Requires
- php: >=8.2
- craftcms/cms: ^5.0.0
- dev-main
- 1.4.1
- 1.4.0
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.1
- 1.2.0
- 1.1.0
- 1.0.1
- 1.0.0
- dev-feature/select-all-volumes
- dev-bugfix/scan-large-volumes-user-photo
- dev-bugfix/standardize-logging
- dev-bugfix/harden-scan-storage
- dev-bugfix/file-backed-scan-pipeline
- dev-feature/csv-queue-export-and-asset-preview
- dev-feature/queue-scan
This package is auto-updated.
Last update: 2026-06-08 20:43:25 UTC
README
Identify and clean up unused assets in Craft CMS 5.
Features
- View Usage Button - See where any asset is used directly from the asset edit page
- Utilities Page - Scan selected volumes for unused assets with bulk actions
- Staged Background Scanning - Scans run as chained queue jobs with dedicated setup, relations, content, and finalize stages
- Configurable Scan Storage - Choose between file-based and database-based scan storage
- File-Backed Scan State - Long-running scans can store progress and intermediate data under
@storage/asset-cleaner/scans/<scanId>or a shared custom workspace path - Database-Backed Scan State - Containerized and cloud-style installs can store scan state in the database when shared filesystem access is not guaranteed
- Configurable Draft & Revision Handling - Control whether draft-only and revision-only usage should count during scans
- Relational Fallback Option - Optionally treat any row in Craft’s relations table as usage for safer scans in projects with plugin-defined or unknown element types
- Large Library Friendly - Asset snapshots are chunked and content is processed in batches for much better performance on large datasets
- Per-Volume Results - Results grouped by volume with individual file counts and total sizes
- Export Options - Download CSV or ZIP of unused assets with smart filenames
- Folder Structure Option - Choose to preserve folder structure or flatten files in ZIP downloads
- Soft Delete (Trash) - Move assets to trash for safe removal with recovery option
- Permanent Delete - Permanently delete assets with double confirmation
- Progress Indicator - Visual progress bar during scanning, including stage-aware progress messages
- Memory-Efficient ZIP Creation - Handles large files without loading them fully into memory
- CLI Commands - Scan, export, and delete from the command line
Requirements
- Craft CMS 5.0.0 or later
- PHP 8.2 or later
Installation
Via Composer
composer require yannkost/craft-asset-cleaner php craft plugin/install asset-cleaner
Manual / local path repository
- Copy the plugin to your
plugins/directory, for example toplugins/asset-cleaner - Add a path repository to your project's
composer.json:
{
"repositories": {
"asset-cleaner": {
"type": "path",
"url": "plugins/asset-cleaner"
}
}
}
- Require the package and install the plugin:
composer require yannkost/craft-asset-cleaner php craft plugin/install asset-cleaner
Usage
Control Panel
- Navigate to Utilities → Asset Cleaner
- Select the volumes you want to scan
- Choose whether to Include drafts in this scan and/or Include revisions in this scan
- Choose whether to Count all relational references as usage
- Click Scan Now
- Wait for the background scan to complete
- Review the results grouped by volume, showing:
- Number of unused assets per volume
- Total file size per volume
- Use bulk actions (global or per-volume):
- Download CSV - Export a list of unused assets
- Download ZIP - Download unused assets, optionally preserving folder structure
- Put into Trash - Soft delete assets
- Delete Permanently - Permanently remove assets with confirmation
The draft and revision toggles on the utility page override the plugin's default draft/revision handling settings for that individual scan. The relational fallback checkbox is scan-specific and is enabled by default for safer cleanup scans.
Scan stages
The utility page shows progress while the scan moves through these stages:
- Preparing asset snapshot - Builds the stored snapshot of assets in scope
- Scanning relations - Collects asset usage from Craft's relations table
- Scanning content - Scans relevant rich text fields and globals in batches
- Finalizing results - Merges usage data and writes the final unused asset results
ZIP Download Options
When downloading a ZIP file, you can choose:
- Flat - All files in the ZIP root
- Preserve folder structure - Files organized by
volume-handle/folder/path/filename
Smart Filenames
Exported files include meaningful names with timestamps:
- CSV:
unused-assets_volume-name_2024-01-15_14-30-00.csv - ZIP:
unused-assets_volume1__volume2_2024-01-15_14-30-00.zip
View Usage Button
On any asset edit page, click the View Usage button to see:
- Entries using the asset via Asset fields
- Entries referencing the asset in Redactor/CKEditor fields
- Global sets referencing the asset in supported rich text fields
CLI Commands
# Scan all volumes php craft asset-cleaner/scan # Scan specific volumes php craft asset-cleaner/scan --volumes=images,files # Export unused assets to CSV php craft asset-cleaner/scan/export # Export specific volumes php craft asset-cleaner/scan/export --volumes=images # Delete unused assets (with confirmation) php craft asset-cleaner/scan/delete # Delete without confirmation php craft asset-cleaner/scan/delete --force # Dry run (preview only) php craft asset-cleaner/scan/delete --dry-run
How It Works
The plugin identifies unused assets by performing several checks across your Craft CMS site:
- Relations Table - Asset field references stored in Craft's relations table
- Content Fields - Scans Redactor and CKEditor fields for embedded asset references
- Global Sets - Checks supported global set content fields for asset references
- Nested Entries - Resolves nested entries to top-level entries for usage reporting
For large scans, the plugin uses a staged pipeline:
- Snapshot assets in scope into the active scan storage backend
- Build usage sets once from relations and content instead of rescanning all content for every asset
- Process entries in batches rather than loading the full content dataset into memory
- Persist final unused results so completed scans can be restored and exported
The matching strategy prefers stronger signals first:
data-asset-idreferences#asset:<id>references- normalized URL/path matches
- unique filename fallback matches
The plugin can store transient scan state either on disk or in dedicated database tables, depending on your installation requirements.
Technical Details
Scan storage modes
Asset Cleaner supports two scan storage modes:
File-based storage
By default, scans are stored in a file-backed workspace under:
@storage/asset-cleaner/scans/<scanId>/
You can override the workspace base path with either:
- the
ASSET_CLEANER_SCAN_PATHenvironment variable - a
scanWorkspacePathvalue inconfig/asset-cleaner.php - the plugin settings in the control panel
Example plugin config:
<?php return [ 'scanWorkspacePath' => '$ASSET_CLEANER_SCAN_PATH', ];
Typical file-based scan artifacts include:
meta.jsonprogress.json- asset chunk files
- relation/content usage ID files
- final unused asset results
File-based storage is a good default on single-server installs or setups where web and queue workers share the same filesystem path.
Database-based storage
Database-based storage keeps scan state, asset snapshots, used asset IDs, and final results in dedicated database tables instead of the filesystem.
This mode is recommended for:
- containerized environments
- cloud-style installs
- setups where web and queue workers do not share a reliable filesystem
You can switch between file-based and database-based storage in the plugin settings, and config file values can override those settings when needed.
Draft and revision handling
Asset Cleaner lets you control whether assets referenced only in drafts or only in revisions should count as used.
There are three levels of control:
- Config override -
includeDraftsByDefault/includeRevisionsByDefaultinconfig/asset-cleaner.php, or theASSET_CLEANER_INCLUDE_DRAFTS/ASSET_CLEANER_INCLUDE_REVISIONSenvironment variables - Plugin setting - sets the default behavior for new scans when no config override is present
- Utility page toggles - let you override those defaults for an individual scan
Example config override:
<?php return [ 'includeDraftsByDefault' => '$ASSET_CLEANER_INCLUDE_DRAFTS', 'includeRevisionsByDefault' => '$ASSET_CLEANER_INCLUDE_REVISIONS', ];
By default, draft-only and revision-only usage are excluded unless you enable them.
Relational fallback
Scans also provide a Count all relational references as usage checkbox.
When this option is enabled, any asset with a row in Craft’s relations table is treated as used. This is the safest mode for projects that rely on plugin-defined or unknown element types that may store asset relations outside normal entry content.
When this option is disabled, Asset Cleaner uses stricter relation resolution rules to identify more potentially unused assets, but that can undercount legitimate backend-only usage from third-party element types.
Memory-efficient ZIP creation
ZIP exports are built in a memory-friendly way:
- Files are streamed in chunks
- Temporary files are used instead of loading entire assets into memory
- Large files can be exported without exhausting PHP memory
Permissions
The Asset Cleaner utility requires the utility:asset-cleaner permission. Assign this permission to user groups that should have access to the utility.
License
The Craft License. See LICENSE.md for details.
Support
Changelog
See CHANGELOG.md for release notes.