Technical Deep Dive: How We Enforce SaaS Limits in a WordPress Multisite Platform (Without Slowing Anything Down)
November 21, 2025 • Bojan
November 21, 2025 • Bojan

If you’re building a real SaaS on top of WordPress Multisite, you eventually hit the same wall everyone hits:
How do you enforce plan limits reliably without breaking performance, risking data drift, or making the UX feel like a hack?
Most people duct-tape together a handful of actions, throw a few if checks into functions.php, and hope for the best.
We don’t do “hope for the best.”
At SampleHQ — a multi-tenant SaaS built fully on WordPress Multisite — we built a proper, predictable, enforceable limit system:
storage quotas, user limits, sample limits, image optimization, and bulk import throttling.
Here’s the full breakdown of how it works under the hood.
Every rule, every limit, every cap lives in one place:
/functions/plan-restrictions.php
It’s the brain of the entire system.
sf_get_site_plan()'light' or 'pro'. Defaults to 'light' if someone bypasses setup.sf_get_user_limit() / sf_get_sample_limit()'unlimited' for Pro.sf_get_storage_limit()This gives us something every SaaS needs:
One file to modify, test, and reason about
No scattered conditionals
No “magic rules” duplicated across plugins
Most platforms track usage with a database counter.
It works until it doesn’t.
One SFTP delete… one batch operation… one misfired plugin… and the counters drift.
We don’t rely on counters — we scan the tenant’s upload directory in real time:
function sf_get_current_storage_usage() {
$upload_dir = wp_upload_dir();
$upload_path = $upload_dir['basedir'];
$total_size = 0;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($upload_path, RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
if ($file->isFile()) {
$total_size += $file->getSize();
}
}
return $total_size;
}
RecursiveIteratorIterator is memory-efficient and surprisingly fast.This is the part where developers usually comment:
“Wait… WordPress can do that?”
Yes. It can. It just usually doesn’t.
A single unoptimized 10MB JPEG can eat 2% of a Light plan’s storage.
So every image upload goes through:
sf_optimize_uploaded_image()
We resize and compress immediately after WordPress creates the attachment:
imagejpeg($resized, $file_path, $quality);
It’s invisible, automatic, and arguably one of the most important UX improvements in the platform.
If the Owner hits the limit → they see:
“You’ve reached your plan limit. Upgrade to Pro.”
If a Manager hits the limit → they see:
“You’ve reached your plan limit. Please contact the Owner.”
The function behind it:
sf_get_user_limit_message()
This isn’t UI fluff — it’s revenue design.
Bulk imports are the #1 way users accidentally (or intentionally) bypass limits.
So we validate every row during CSV processing:
foreach ($rows as $row_index => $row) {
if ($import_type === 'users' && !sf_can_create_user()) {
$limit_reached = true;
}
if ($limit_reached) {
$results['failed'][] = [
'error' => 'Plan limit reached...',
'reason' => 'limit_reached'
];
break;
}
// process normally
}
This is the difference between “a plugin” and a platform.
WordPress Multisite gives you an incredible foundation for building a SaaS — but only if you treat it like an engineering platform, not a blog CMS.
Limit enforcement is one of those areas where most SaaS products built on WordPress fall apart:
drift-prone counters
inconsistent rules
no messaging hierarchy
unoptimized uploads
loopholes through imports
SampleHQ solves this with:
If you want WordPress to feel like a real SaaS, this is the kind of engineering discipline that makes it possible.

I’m Bojan Josifoski - I’m a WordPress systems engineer who developed and maintained a proprietary WordPress-based framework used by U.S. financial institutions between 2016 and 2025.