SharePoint versioning is genuinely useful — every edit gets its own recoverable snapshot — but nobody ever prunes it by default. A library with hundreds of edits per file over a few years can be carrying gigabytes of version history nobody’s looked at, pushing storage usage up and query performance down. Cleaning it up periodically is routine maintenance, not an edge case.
In this post: Major vs minor versions · Prerequisites · PowerShell for SharePoint Online · PowerShell for SharePoint On-Premises · Best practices · When would you actually use this? · Related reading
Major vs minor versions
Major versions (1.0, 2.0, 3.0) represent published, official states of a document — one gets created every time you save, in libraries where that’s the only versioning mode enabled. Minor versions (1.1, 1.2) track interim drafts, and only appear when content approval is turned on — they’re visible to editors, not to everyone with just read access. Both count toward the version total that eventually needs pruning; minor versions especially, since drafts accumulate faster than published saves.
Prerequisites
- Full Control or Site Collection Administrator permissions — version deletion isn’t available at lower permission levels.
- A backup of the library or list before running anything in bulk. Deleted versions aren’t recoverable from the Recycle Bin the way a deleted item is.
PnP.PowerShellinstalled for SharePoint Online, or the SharePoint Management Shell / snap-in for on-premises.- A decided retention number before you start — most organizations settle on keeping 5-10 major versions and pruning the rest, but pick a number deliberately rather than defaulting to whatever the script happens to use.
PowerShell for SharePoint Online
# Install PnP PowerShell Module (if not installed)
# Install-Module -Name "PnP.PowerShell" -Scope CurrentUser
# Connect to SharePoint Online
$siteUrl = "https://yourtenant.sharepoint.com/sites/yoursite"
Connect-PnPOnline -Url $siteUrl -Interactive
# Set variables
$libraryName = "Documents"
$keepVersions = 5
# Get all items in the library
$items = Get-PnPListItem -List $libraryName -PageSize 500
foreach ($item in $items) {
$file = Get-PnPFile -Url $item.FieldValues["FileRef"] -AsListItem
$versions = $file.Versions
if ($versions.Count -gt $keepVersions) {
# Delete from the end backward -- deleting forward by index while the
# collection re-indexes on the server can skip entries.
for ($i = $versions.Count - 1; $i -ge $keepVersions; $i--) {
$label = $versions[$i].VersionLabel
$versions[$i].DeleteObject()
Invoke-PnPQuery
Write-Host "Deleted version $label of $($file.Name)"
}
}
}
Disconnect-PnPOnline
One change from how this is often written: delete from the highest index down to $keepVersions, not the other way around. Deleting forward while the version collection re-indexes on each Invoke-PnPQuery call can silently skip versions — going backward avoids that entirely.
PowerShell for SharePoint On-Premises
# Load SharePoint Snap-in
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
# Variables
$webUrl = "http://yoursite"
$listName = "Documents"
$keepVersions = 5
# Get the web and list
$web = Get-SPWeb $webUrl
$list = $web.Lists[$listName]
# Loop through items and delete old versions, newest-kept to oldest
foreach ($item in $list.Items) {
if ($item.Versions.Count -gt $keepVersions) {
for ($i = $item.Versions.Count - 1; $i -ge $keepVersions; $i--) {
$item.Versions[$i].Delete()
}
$item.Update()
Write-Host "Deleted old versions for: $($item.Name)"
}
}
$web.Dispose()
Best practices
- Test against a sandbox site or a copy of the library first — version deletion has no undo, and running an untested script against production is how “clean up old versions” becomes an incident.
- Automate it on a schedule rather than running it manually whenever storage complaints come in — version bloat is continuous, not a one-time cleanup.
- Set a version limit in library settings (Versioning Settings > “Keep the following number of major versions”) as the long-term fix — scripts handle the backlog, but a configured limit prevents it from recurring.
- Check for workflow or approval dependencies before bulk deletion — some processes reference specific version numbers, and deleting under them can break more than storage.
Set a version limit in library settings as the actual fix — scripts clean up the backlog, but only a configured cap stops it from building up again.
When would you actually use this?
- SharePoint Online storage is creeping toward its quota and a handful of heavily-edited libraries are the likely cause.
- A large list or library has gotten noticeably slower to load, and version count is a plausible contributor worth ruling out.
- Compliance requires that only a defined retention window of versions exists, not an unbounded history.
Related reading
- Extracting SharePoint File Version History Using PowerShell — audit what’s actually there before deciding what to delete.
- Exploring SharePoint Files and List Item Versioning — the version-limit settings referenced in Best Practices above, covered in full.
That’s the cleanup covered. Comment below if your version counts still look off after running this.
App Catalog Authentication Automation Backup Compliance Content Type CSS Flows Google Javascript Limitations List Metadata MFA Microsoft Node NodeJs O365 OneDrive Permissions PnP PnPJS Policy PowerApps Power Automate PowerAutomate PowerPlatform PowerShell React ReactJs Rest API Rest Endpoint Security Send an HTTP Request to SharePoint SharePoint SharePoint List SharePoint Modern SharePoint Online SPFX SPO Sync Tags Teams Termstore Versioning


