Squid Realtime Status by way if sqlight SQStat
-
I wanted to share this with anyone else that noticed the speed when you hover over connections on status shows 0/0 always. Just in case also if anyone has any recommendations let me know.
sqstat.class.php - Changes Log
pfSense Squid Realtime Stats (SQStat) Speed Fix
Date: March 12, 2026PROBLEM SUMMARY
Current Speed and Avg Speed columns always showed 0 in SQStat.
Three root causes were identified and fixed.
CHANGE 1: Connection ID Key (both makeHtmlReport and parseRequest)
LOCATION: makeHtmlReport (~line 341) and parseRequest (~line 499)
BEFORE:
$con_id = $con['connection'];AFTER:
$con_id = md5($con['uri'] . $con['peer']);WHY:
Squid reports connection IDs as memory addresses (e.g. 0xbe97ec98)
which change on every request. This meant the session key for a
connection never matched between page loads, so previous byte counts
could never be found and current speed was always 0.
Using md5(uri + peer) creates a stable, consistent key that matches
the same connection across multiple refreshes.
CHANGE 2: Session replaced with temp file (both functions)
LOCATION: makeHtmlReport (~line 280) and parseRequest (~line 460)
BEFORE (session read):
unset($session_data);
if (isset($_SESSION['time']) && ((time() - $_SESSION['time']) < 3*60)
&& isset($_SESSION['sqdata']) && is_array($_SESSION['sqdata'])) {
$session_data = $_SESSION['sqdata'];
}AFTER (file read):
$sqstat_file = '/tmp/sqstat_data.json';
$session_data = array();
if (file_exists($sqstat_file) && (time() - filemtime($sqstat_file)) < 180) {
$session_data = json_decode(file_get_contents($sqstat_file), true) ?: array();
}BEFORE (session write):
$_SESSION['time'] = time();
if (isset($new_data)) {
$_SESSION['sqdata'] = $new_data;
}AFTER (file write):
if (isset($new_data)) {
file_put_contents($sqstat_file, json_encode($new_data));
}WHY:
pfSense uses jQuery AJAX to refresh SQStat. Each AJAX call was
receiving a new PHP session ID, meaning $_SESSION was always empty
on every refresh. Data written to $_SESSION on one call was never
available on the next call. This was confirmed by debug logging
showing a different session_id() on every request.
/tmp on pfSense is a tmpfs (RAM) filesystem so writing to
/tmp/sqstat_data.json has zero SSD impact and persists correctly
between AJAX calls.
CHANGE 3: Session start blocks removed (both functions)
LOCATION: makeHtmlReport (~line 213) and parseRequest (~line 395)
BEFORE:
if ($this->use_sessions) {
if (session_status() == PHP_SESSION_NONE) {
session_name('SQDATA');
session_start();
}
}AFTER:
(removed entirely)WHY:
pfSense already starts its own PHP session before SQStat loads.
Attempting to start a second session named 'SQDATA' was conflicting
with pfSense's session management. Since we moved to file-based
storage this code is no longer needed at all.
CHANGE 4: Avg speed moved outside session check (parseRequest)
LOCATION: parseRequest (~line 520)
BEFORE:
if (isset($session_data[$con_id]) && !empty($session_data[$con_id])) {
// ... curr_speed calculation ...
// avg speed
$avg_speed = $con['bytes'] / 1024;
if ($con['seconds'] > 0) {
$avg_speed /= $con['seconds'];
}
}AFTER:
if (isset($session_data[$con_id])) {
// ... curr_speed calculation only ...
}
// avg speed - always calculate
if ($con['bytes'] > 0 && $con['seconds'] > 0) {
$avg_speed = ($con['bytes'] / 1024) / $con['seconds'];
}WHY:
Avg speed does not need previous request data - it can always be
calculated as total bytes transferred divided by connection duration.
By moving it outside the session/file data check it shows immediately
on first load for any connection alive more than 1 second, without
needing a previous snapshot.
CHANGE 5: Current speed calculation simplified (both functions)
LOCATION: makeHtmlReport (~line 345) and parseRequest (~line 503)
BEFORE:
if ($was_time && $was_size) {
$delta = $is_time - $was_time;
if ($delta == 0) {
$delta = 1;
}
if ($con['bytes'] >= $was_size) {
$curr_speed = ($con['bytes'] - $was_size) / 1024 / $delta;
}
} else {
$curr_speed = $con['bytes'] / 1024;
}AFTER:
$delta_time = max(1, $is_time - $was_time);
$delta_bytes = $con['bytes'] - $was_size;
if ($delta_bytes > 0) {
$curr_speed = ($delta_bytes / 1024) / $delta_time;
}WHY:
Simplified the delta calculation using max(1, ...) to avoid division
by zero more cleanly. Removed the fallback that set curr_speed to
total bytes when no previous size was recorded - that was showing
inflated incorrect values. Now only shows current speed when there
is a genuine positive byte delta between refreshes.
NOTES
- /tmp/sqstat_data.json is written on every refresh (tmpfs = RAM, no SSD writes)
- Old closed connections remain in the JSON file but are ignored since
they won't match any active Squid connection on the next poll - makeHtmlReport is not used by pfSense's version of SQStat (pfSense uses
parseRequest + sqstat_resultHTML via AJAX) but was updated for consistency - Current speed requires at least 2 refreshes with the same connection
active to show a value - this is expected behavior - Avg speed shows immediately on first load for connections > 1 second old
===============================
End of change log