Footer Hit Counter Badge (No DB)

January 14, 2026 NEW

Here's a tiny “stats icon” you can drop into your site footer that counts page hits and shows a small badge like 👁 12,345.

What it does:

  • Counts total hits across your site (simple and lightweight).
  • Stores the count in a single local file (no database).
  • Uses file locking to avoid race conditions.
  • Optionally ignores bots and admin pages.

Install:

  1. Create: /tools/hitbadge/
  2. Add these 2 files inside it:
    • hit.php (increments & prints the badge HTML)
    • count.txt (an empty file — writable)
  3. In your footer, paste: <?php include $_SERVER['DOCUMENT_ROOT']."/tools/hitbadge/hit.php"; ?>

Tip: If you want “per-page” counts later, you can expand this to hash the URL and store multiple counters. This version stays intentionally tiny.

<?php
declare(strict_types=1);

/**
 * /tools/hitbadge/hit.php
 * Tiny Footer Hit Counter Badge (No DB)
 *
 * - Increments a total hit counter (count.txt)
 * - Prints a small footer badge
 *
 * Include in your footer:
 *   <?php include $_SERVER['DOCUMENT_ROOT']."/tools/hitbadge/hit.php"; ?>
 */

// ---------------- CONFIG ----------------
$COUNT_FILE = __DIR__ . '/count.txt';

// Optional: ignore these URL prefixes
$IGNORE_PATHS = ['/admin', '/private', '/tools/hitbadge'];

// Optional: basic bot ignore (very simple)
$IGNORE_BOTS = true;

// Badge label/icon
$LABEL = 'Views';
$ICON  = '👁';
// --------------------------------------

function is_bot(string $ua): bool {
  $ua = strtolower($ua);
  $needles = ['bot','spider','crawl','slurp','bingpreview','facebookexternalhit','archive','fetch','lighthouse'];
  foreach ($needles as $n) {
    if (strpos($ua, $n) !== false) return true;
  }
  return false;
}

function should_ignore(array $ignorePaths, bool $ignoreBots): bool {
  $uri = $_SERVER['REQUEST_URI'] ?? '/';
  foreach ($ignorePaths as $p) {
    if ($p !== '' && strpos($uri, $p) === 0) return true;
  }

  if ($ignoreBots) {
    $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
    if ($ua !== '' && is_bot($ua)) return true;
  }

  return false;
}

function read_int(string $file): int {
  if (!is_file($file)) return 0;
  $raw = trim((string)@file_get_contents($file));
  if ($raw === '' || !ctype_digit($raw)) return 0;
  return (int)$raw;
}

function write_int_locked(string $file, int $n): bool {
  $fh = @fopen($file, 'c+'); // create if missing
  if (!$fh) return false;

  if (!flock($fh, LOCK_EX)) { fclose($fh); return false; }

  rewind($fh);
  $raw = stream_get_contents($fh);
  $cur = 0;
  if (is_string($raw)) {
    $raw = trim($raw);
    if ($raw !== '' && ctype_digit($raw)) $cur = (int)$raw;
  }

  $cur++;

  rewind($fh);
  ftruncate($fh, 0);
  fwrite($fh, (string)$cur);
  fflush($fh);

  flock($fh, LOCK_UN);
  fclose($fh);

  return true;
}

function badge_html(string $icon, string $label, int $count): string {
  $countFmt = number_format($count);
  return '<span class="hitbadge" title="'.htmlspecialchars($label,ENT_QUOTES,'UTF-8').'">'
    . '<span class="hitbadge-ico" aria-hidden="true">'.htmlspecialchars($icon,ENT_QUOTES,'UTF-8').'</span>'
    . '<span class="hitbadge-num">'.htmlspecialchars($countFmt,ENT_QUOTES,'UTF-8').'</span>'
    . '</span>'
    . '<style>'
    . '.hitbadge{display:inline-flex;align-items:center;gap:6px;'
    . 'padding:6px 10px;border-radius:999px;'
    . 'border:1px solid rgba(255,255,255,.14);'
    . 'background:rgba(255,255,255,.06);'
    . 'font:700 12px/1 system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;'
    . 'color:inherit}'
    . '.hitbadge-ico{font-size:13px;opacity:.9}'
    . '.hitbadge-num{letter-spacing:.2px}'
    . '</style>';
}

// If ignored, just display current count without incrementing
if (should_ignore($IGNORE_PATHS, $IGNORE_BOTS)) {
  $count = read_int($COUNT_FILE);
  echo badge_html($ICON, $LABEL, $count);
  return;
}

// Increment (locked), then read back
if (!write_int_locked($COUNT_FILE, 0)) {
  // fail open: show placeholder if file isn't writable
  echo '<span class="hitbadge" style="font:700 12px system-ui">👁 —</span>';
  return;
}

$count = read_int($COUNT_FILE);
echo badge_html($ICON, $LABEL, $count);
?>

Comments (0)

No comments yet — be the first.

← Back to all scripts