آمار و تحلیل

آمار بازدید سایت بدون گوگل آنالیتیکس — از لاگ سرور تا آی‌پی واقعی کاربر

نمودار ساعتی بازدید سایت و سهم دستگاه‌ها در گزارش GoAccess روی لاگ وب‌سرور

سه ابزار مختلف را باز می‌کنید و سه عدد کاملاً متفاوت می‌بینید: لاگ سرور می‌گوید ۴۰ هزار، اسکریپت آمارگیر می‌گوید ۹ هزار و داشبورد کلادفلر عدد سومی نشان می‌دهد. هیچ‌کدام خراب نیستند — هر سه چیزهای متفاوتی می‌شمارند. در این راهنما یاد می‌گیرید آمار بازدید سایت را روی سرور خودتان و بدون گوگل آنالیتیکس درست اندازه بگیرید، و مهم‌تر از آن: بفهمید کدام عدد را باور کنید.

چهار عددی که همه با هم اشتباه گرفته می‌شوند

قبل از هر دستوری باید سر واژه‌ها توافق کنیم، چون بیشتر دعواهای «آمار سایتم غلط است» از همین‌جا شروع می‌شود:

  • Hit (درخواست) — هر درخواست HTTP که به سرور می‌رسد. یک صفحه‌ی وردپرسی معمولی به‌تنهایی ۳۰ تا ۸۰ درخواست تولید می‌کند: HTML، چند فایل CSS و JS، فونت‌ها، تصاویر، فاوآیکون. این عدد بزرگ‌ترین و بی‌معنی‌ترین عدد آمار شماست.
  • Page view (بازدید صفحه) — فقط درخواست‌هایی که یک سند HTML برگردانده‌اند. این عددی است که وقتی می‌گویید «امروز چند بازدید داشتم» منظورتان است.
  • Session (نشست) — یک دنباله از بازدیدهای پشت‌سرهم از یک نفر. قرارداد صنعتی این است: اگر بین دو درخواست بیشتر از ۳۰ دقیقه فاصله بیفتد، نشست جدیدی شروع شده. این عدد قراردادی است، نه حقیقت طبیعت.
  • Unique visitor (بازدیدکننده‌ی یکتا) — تعداد آدم‌های متمایز. این تنها عددی است که واقعاً می‌خواهید و دقیقاً همانی است که هیچ ابزاری نمی‌تواند دقیق بدهد؛ فقط تخمین‌های خوب و بد داریم.

حالا معلوم می‌شود چرا منابع مختلف هم‌دیگر را تأیید نمی‌کنند:

منبع آمارچه چیزی را می‌شماردچه چیزی را از دست می‌دهد
لاگ وب‌سرورهر درخواستی که به سرور اصلی رسیده — انسان، ربات، اسکنر، همهبازدیدهایی که کش CDN جواب داده و اصلاً به سرور نرسیده‌اند
اسکریپت JS (آنالیتیکس)فقط مرورگرهای واقعی که جاوااسکریپت را اجرا کرده‌اندکاربران ادبلاک‌دار، کسانی که قبل از اجرای اسکریپت صفحه را بسته‌اند، و ربات‌ها (که این‌جا یک مزیت است)
داشبورد کلادفلرهمه‌ی درخواست‌های لبه، شامل فایل‌های استاتیک کش‌شدهتفکیک انسان از ربات را با مدل تخمینی انجام می‌دهد، نه قطعی

نسبت‌های واقعی که روی سایت‌های کوچک فارسی می‌بینید: تعداد Hit معمولاً ۱۰ تا ۲۰ برابر Page view است، و آمارگیر جاوااسکریپتی چیزی حدود ۲۰ تا ۴۰ درصد کمتر از لاگِ ربات‌پاک‌شده گزارش می‌دهد. اگر این فاصله‌ها را ببینید، همه‌چیز سالم است.

💡 نکته: قبل از مقایسه‌ی دو ابزار، منطقه‌ی زمانی‌شان را یکی کنید. سرورهای اروپایی پیش‌فرض روی UTC اجرا می‌شوند و ۳.۵ ساعت با تهران اختلاف دارند؛ همین باعث می‌شود «آمار دیروز» در دو ابزار دو عدد شود. timedatectl set-timezone Asia/Tehran

اول از همه: لاگ خام را با چشم خودتان بخوانید

قبل از نصب هر داشبوردی، نیم ساعت با لاگ خام وقت بگذارید؛ هیچ ابزاری آن تصویر خامِ ترافیک واقعی را نمی‌دهد. مسیر لاگ بسته به وب‌سرور:

/var/log/nginx/access.log            # Nginx
/var/log/apache2/access.log          # Apache (Debian/Ubuntu)
/var/log/httpd/access_log            # Apache (AlmaLinux/Rocky)
~/access-logs/example.com            # cPanel / LiteSpeed

قالب استاندارد combined که تقریباً همه‌جا فعال است، هر خط را این‌طور می‌نویسد:

203.0.113.9 - - [30/Jul/2026:14:02:11 +0330] "GET /blog/vps HTTP/2.0" 200 18432 "https://www.google.com/" "Mozilla/5.0 (Linux; Android 14) ..."

یعنی برای awk: ستون ۱ آی‌پی، ستون ۴ زمان، ستون ۷ مسیر، ستون ۹ کد وضعیت، ستون ۱۰ حجم پاسخ، ستون ۱۱ ارجاع‌دهنده و از ستون ۱۲ به بعد مرورگر. (اگر با awk و sort راحت نیستید، مرجع دستورات پرکاربرد لینوکس را کنار دستتان بگذارید.)

بازدید صفحه به تفکیک روز

ترفند اصلی این است که فایل‌های استاتیک و پاسخ‌های ناموفق را کنار بگذارید؛ چیزی که باقی می‌ماند بازدید صفحه است:

awk '$9 ~ /^(200|304)$/ && $7 !~ /\.(css|js|png|jpe?g|gif|svg|webp|ico|woff2?|map|xml|txt)($|\?)/ {
       split($4, d, ":"); gsub(/\[/, "", d[1]); print d[1]
     }' /var/log/nginx/access.log | sort | uniq -c
   3184 28/Jul/2026
   2971 29/Jul/2026
   3402 30/Jul/2026

پربازدیدترین صفحه‌ها

awk '$9 == 200 && $7 !~ /\.(css|js|png|jpe?g|gif|svg|webp|ico|woff2?)/ {print $7}' \
    /var/log/nginx/access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head -20

حذف کوئری‌استرینگ با sed مهم است، وگرنه /blog?utm_source=telegram و /blog دو صفحه‌ی جدا شمرده می‌شوند و آمارتان تکه‌تکه می‌شود.

نمودار ساعتی ترافیک

awk '{split($4, d, ":"); print d[2]":00"}' /var/log/nginx/access.log \
    | sort | uniq -c | awk '{printf "%s %5d %s\n", $2, $1, substr("########################################", 1, $1/50)}'

خروجی یک هیستوگرام متنی است که در چند ثانیه به شما می‌گوید اوج ترافیک سایتتان کِی است — همان عددی که تصمیم می‌گیرد بکاپ و کرون‌های سنگین را چه ساعتی بگذارید.

پرترافیک‌ترین آی‌پی‌ها و شمارش یکتا

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
awk '{print $1}' /var/log/nginx/access.log | sort -u | wc -l

خروجی دستور اول را خوب نگاه کنید؛ اگر سایت پشت کلادفلر باشد این فهرست عملاً بی‌معنی است — بخش بعدی دقیقاً درباره‌ی همین است.

تخمین نشست‌ها با قاعده‌ی ۳۰ دقیقه

این همان کاری است که هر ابزار آنالیتیکسی پشت پرده می‌کند. توجه: به gawk نیاز دارد چون mawk (پیش‌فرض اوبونتو) تابع mktime ندارد:

sudo apt install gawk

gawk -v gap=1800 '
BEGIN { split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", m, " ")
        for (i = 1; i <= 12; i++) mon[m[i]] = i }
{ t = $4; sub(/^\[/, "", t); split(t, p, /[\/:]/)
  ts = mktime(p[3]" "mon[p[2]]" "p[1]" "p[4]" "p[5]" "p[6])
  if (ts - last[$1] > gap) s++
  last[$1] = ts }
END { print "approx sessions:", s }' /var/log/nginx/access.log

کلمه‌ی approx در خروجی تعارف نیست: این تخمین آی‌پی را به‌جای آدم گرفته است و همین، بزرگ‌ترین اشتباه ممکن در ایران است. دو بخش بعدی دقیقاً درباره‌ی همین است.

تله‌ی اصلی: پشت کلادفلر، همه‌ی بازدیدکننده‌ها یک نفرند

این پرتکرارترین خطای آمار بازدید در ایران است و علامتش کاملاً مشخص: فهرست پرترافیک‌ترین آی‌پی‌ها را می‌گیرید و می‌بینید پنج شش آی‌پی هرکدام ده‌ها هزار درخواست دارند و بقیه‌ی فهرست خالی است. نتیجه می‌گیرید سایتتان زیر حمله است. نیست.

وقتی سایت پشت کلادفلر (یا هر CDN و پراکسی معکوس دیگری) است، مرورگر به سرور لبه وصل می‌شود و کلادفلر به سرور شما. پس اتصالی که سرور شما می‌بیند از کلادفلر آمده و REMOTE_ADDR — همان ستون اول لاگ — آی‌پی سرور لبه است، نه آی‌پی بازدیدکننده.

آی‌پی واقعی گم نشده؛ کلادفلر آن را در یک هدر HTTP می‌گذارد:

CF-Connecting-IP: 5.160.x.x          # فقط کلادفلر
X-Forwarded-For: 5.160.x.x, 172.68.x.x   # استاندارد عمومی، زنجیره‌ای
⚠️ و حالا مهم‌ترین جمله‌ی این مقاله: هرگز این هدرها را کورکورانه باور نکنید. هدر HTTP فقط یک متن است که فرستنده می‌نویسد. اگر کسی آی‌پی واقعی سرور شما را پیدا کند و بتواند مستقیم به آن وصل شود، با یک curl -H "CF-Connecting-IP: 8.8.8.8" هر آی‌پی دلخواهی را در لاگ و آمار شما ثبت می‌کند — و اگر همان هدر مبنای محدودیت نرخ یا بن‌کردن کاربر باشد، تبدیل به یک آسیب‌پذیری واقعی می‌شود.

راه‌حل درست یک شرط ساده است: اول بررسی کن اتصال از داخل محدوده‌های اعلام‌شده‌ی کلادفلر آمده باشد، بعد هدر را بخوان. اگر اتصال از جای دیگری است، هدر را دور بینداز.

Nginx — با ماژول real_ip

این ماژول به‌صورت پیش‌فرض در Nginx کامپایل شده است. یک فایل کانفیگ بسازید و فهرست رسمی کلادفلر را مستقیم از خودش بگیرید:

curl -s https://www.cloudflare.com/ips-v4 | sed 's|^|set_real_ip_from |; s|$|;|' >  /etc/nginx/conf.d/cloudflare.conf
curl -s https://www.cloudflare.com/ips-v6 | sed 's|^|set_real_ip_from |; s|$|;|' >> /etc/nginx/conf.d/cloudflare.conf
echo 'real_ip_header CF-Connecting-IP;' >> /etc/nginx/conf.d/cloudflare.conf
echo 'real_ip_recursive on;'            >> /etc/nginx/conf.d/cloudflare.conf

nginx -t && systemctl reload nginx

از این لحظه $remote_addr در همه‌جای Nginx — لاگ، limit_req، قوانین دسترسی — خودبه‌خود آی‌پی واقعی بازدیدکننده است و لازم نیست هیچ چیز دیگری را عوض کنید. این فایل را ماهی یک‌بار (مثلاً با یک کرون) دوباره بسازید چون کلادفلر گاهی محدوده اضافه می‌کند. اگر Nginx را تازه راه انداخته‌اید، راهنمای راه‌اندازی Nginx روی اوبونتو بقیه‌ی کانفیگ پایه را پوشش می‌دهد.

Apache — با mod_remoteip

sudo a2enmod remoteip

# /etc/apache2/conf-available/remoteip.conf
RemoteIPHeader CF-Connecting-IP
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 103.21.244.0/22
# ... بقیه‌ی محدوده‌های cloudflare.com/ips-v4

# و این خط حیاتی است: %h را به %a تغییر دهید
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

بدون تغییر %h به %a، ماژول درست کار می‌کند ولی لاگ همچنان آی‌پی پراکسی را می‌نویسد و شما ساعت‌ها دنبال اشکال می‌گردید.

روی LiteSpeed و OpenLiteSpeed همین کار در تنظیمات سرور با گزینه‌ی «Use Client IP in Header» انجام می‌شود؛ حتماً مقدار Trusted IP Only را انتخاب کنید، نه Yes. تفاوت این دو دقیقاً همان تفاوت امن و ناامن است.

نسخه‌ی PHP — وقتی به کانفیگ وب‌سرور دسترسی ندارید

<?php
// The CDN header is meaningful ONLY when the TCP peer is the CDN itself.
// Anyone able to reach the origin directly can forge CF-Connecting-IP.
function ip_in_cidr(string $ip, string $cidr): bool
{
    $parts = explode('/', $cidr);
    $bits  = isset($parts[1]) ? (int) $parts[1] : 32;
    $ipL   = ip2long($ip);
    $netL  = ip2long($parts[0]);
    if ($ipL === false || $netL === false) {
        return false;   // IPv6 needs inet_pton() — handled separately
    }
    $mask = $bits === 0 ? 0 : (-1 << (32 - $bits));
    return ($ipL & $mask) === ($netL & $mask);
}

function client_ip(): string
{
    $peer   = $_SERVER['REMOTE_ADDR'] ?? '';
    $ranges = file(__DIR__ . '/cloudflare-ips-v4.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($ranges as $cidr) {
        if (ip_in_cidr($peer, $cidr)) {
            $fwd = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? '';
            return filter_var($fwd, FILTER_VALIDATE_IP) ? $fwd : $peer;
        }
    }
    return $peer;   // not from the CDN → the header is untrusted, ignore it
}

یک قدم مکمل که خیلی‌ها فراموش می‌کنند: فایروال را طوری ببندید که پورت ۸۰ و ۴۴۳ فقط از محدوده‌های کلادفلر باز باشد. آن‌وقت جعل هدر عملاً غیرممکن می‌شود چون کسی نمی‌تواند مستقیم به origin برسد — روش کار با ufw در چک‌لیست امنیت سرور لینوکس آمده است.

چرا شمارش بر اساس آی‌پی در ایران بی‌اعتبار است

فرض کنید همه‌ی کارهای بالا را کرده‌اید و حالا آی‌پی واقعی بازدیدکننده‌ها را دارید. خبر بد: در ایران این آی‌پی هنوز معیار خوبی برای شمردن آدم‌ها نیست، و به دو دلیلِ متضاد.

یک: آی‌پی چند نفر را یکی می‌کند. اپراتورهای موبایل ایران — همراه اول، ایرانسل، رایتل — از CGNAT استفاده می‌کنند؛ یعنی هزاران مشترک پشت یک آی‌پی عمومی مشترک بیرون می‌آیند. اگر امروز ۵۰۰ نفر با اینترنت همراه اول به سایتتان آمده باشند، ممکن است در لاگ فقط چند ده آی‌پی متمایز ببینید. شمارش آی‌پی یکتا این ۵۰۰ نفر را به چند ده نفر تقلیل می‌دهد.

دو: همان آی‌پی یک نفر را چند نفر می‌کند. یک گوشی وقتی بین دکل‌ها جابه‌جا می‌شود، از وای‌فای به دیتا می‌رود یا فقط چند دقیقه بی‌کار می‌ماند، آی‌پی عمومی‌اش عوض می‌شود. یک کاربر که ده دقیقه در سایت شما بوده، به‌راحتی می‌تواند سه آی‌پی متفاوت در لاگ بگذارد و سه «بازدیدکننده‌ی یکتا» شمرده شود.

روی این‌ها استفاده‌ی گسترده از VPN را هم اضافه کنید: آمار کشور و شهرِ مبتنی بر آی‌پی در سایت‌های فارسی معمولاً نشان می‌دهد بازدیدکننده‌هایتان در آلمان و هلند و ترکیه‌اند. بر اساس آن اعداد تصمیم بازاریابی نگیرید. (برای درک رفتار واقعی شبکه‌ی ایران، مقاله‌ی کاهش پینگ و مسیریابی به ایران تصویر دقیق‌تری می‌دهد.)

نتیجه‌ی عملی این است: فهرست آی‌پی‌ها به سؤال «چه کسی به ما درخواست زد» جواب می‌دهد، نه «چند نفر آمدند». برای امنیت، محدودیت نرخ و پیداکردن اسکنرها عالی است؛ برای شمردن آدم بی‌فایده است.

معیار درست هویت، یک کوکی فرست‌پارتی است — نه CGNAT خرابش می‌کند، نه تعویض دکل، نه VPN:

<?php
// A first-party cookie survives CGNAT, tower handover and VPN switching —
// none of which an IP-based counter can tell apart.
if (empty($_COOKIE['vid'])) {
    setcookie('vid', bin2hex(random_bytes(8)), [
        'expires'  => time() + 400 * 86400,   // Chrome caps cookie lifetime at 400 days
        'path'     => '/',
        'secure'   => true,
        'httponly' => true,
        'samesite' => 'Lax',
    ]);
}

اگر اصلاً کوکی نمی‌خواهید، روش Plausible جایگزین خوبی است: شناسه‌ی روزانه از هش نمک روزانه + آی‌پی + مرورگر + دامنه بسازید و نمک را هر ۲۴ ساعت دور بیندازید. نتیجه این است که بازدیدکننده‌ی یکتای «امروز» را می‌شمارید ولی نمی‌توانید کسی را بین دو روز دنبال کنید — به همین دلیل این ابزارها هیچ‌وقت آمار یکتای ماهانه‌ی واقعی نمی‌دهند.

ابزارهای self-hosted: مقایسه‌ی بی‌تعارف

ابزارروش جمع‌آوریدیتابیسدر برابر ادبلاکمصرف رم تقریبیحداقل سرور
GoAccessخواندن لاگ سرورنداردکاملاً مقاوم — اصلاً جاوااسکریپت ندارد۳۰–۸۰ مگابایت۱ گیگابایت
Umamiاسکریپت ~۲ کیلوبایتیPostgreSQL یا MySQLنسبتاً خوب (اگر از دامنه‌ی خودتان سرو شود)~۱۵۰ مگ Node + ~۲۰۰ مگ دیتابیس۲ گیگابایت
Plausibleاسکریپت ~۱ کیلوبایتیPostgreSQL + ClickHouseنسبتاً خوب۱ تا ۱.۵ گیگابایت (ClickHouse سنگین است)۴ گیگابایت
Matomoاسکریپت + امکان ایمپورت لاگMySQL / MariaDBضعیف — در لیست‌های ادبلاک هدف مستقیم است~۳۰۰ مگ PHP + دیتابیس رشدکننده۲ گیگابایت + کرون آرشیو

انتخاب واقعی این‌طور است: اگر عدد کامل و بدون نشتی می‌خواهید، GoAccess چون از لاگ می‌خواند هیچ‌چیز را از دست نمی‌دهد. اگر می‌خواهید بدانید کاربر واقعاً چه کرد — از کجا آمد، چند صفحه دید، کجا رفت — به ابزار جاوااسکریپتی نیاز دارید و Umami سبک‌ترین گزینه است. Matomo کامل‌ترین و سنگین‌ترین است و بهترین بخشش ایمپورت لاگ (misc/log-analytics/import_logs.py) است. Plausible را فقط با چهار گیگ رم اضافه بردارید؛ ClickHouse روی سرور یک‌گیگی بالا نمی‌آید.

راه‌اندازی عملی GoAccess در دو دقیقه

sudo apt install goaccess

# گزارش یک‌باره به‌شکل یک فایل HTML
goaccess /var/log/nginx/access.log \
  --log-format=COMBINED \
  --ignore-crawlers \
  --anonymize-ip \
  -o /var/www/html/stats/index.html

# داشبورد زنده که خودش با هر درخواست جدید آپدیت می‌شود
goaccess /var/log/nginx/access.log \
  --log-format=COMBINED --ignore-crawlers --real-time-html \
  --ws-url=wss://example.com:7890 \
  --persist --restore --db-path=/var/lib/goaccess \
  -o /var/www/html/stats/index.html &

سه سوییچ مهم: --ignore-crawlers ربات‌های شناخته‌شده را کنار می‌گذارد، --anonymize-ip آخرین بخش آی‌پی را صفر می‌کند و --persist با --restore باعث می‌شود آمار بعد از چرخش لاگ یا ری‌استارت از بین نرود.

⚠️ فایل stats/index.html فهرست کامل مسیرهای سایت، ارجاع‌دهنده‌ها و آی‌پی‌های شما را لو می‌دهد. حتماً پشت احراز هویت بگذاریدش: htpasswd -c /etc/nginx/.htpasswd admin و بعد یک بلاک location /stats { auth_basic "stats"; auth_basic_user_file /etc/nginx/.htpasswd; }.

ابزارهای جاوااسکریپتی را هم بهتر است با داکر بالا بیاورید تا وابستگی‌هایشان با سیستم قاطی نشود؛ روش کار در آموزش داکر روی سرور ابری آمده است.

ربات‌ها: چه سهمی از آمار شما اصلاً آدم نیست

روی یک سایت فارسی متوسط، ۴۰ تا ۶۰ درصد درخواست‌های خام از ربات‌هاست؛ روی سایت تازه‌راه‌افتاده این عدد به‌راحتی از ۸۰ درصد رد می‌شود. ترکیبش هم عوض شده: علاوه بر Googlebot و Bingbot، حالا خزنده‌های هوش مصنوعی (GPTBot، ClaudeBot، PerplexityBot، Bytespider)، ابزارهای سئو (AhrefsBot، SemrushBot) و اسکنرهای امنیتی سهم بزرگی دارند.

سنجش سهم ربات‌ها روی لاگ خودتان:

LOG=/var/log/nginx/access.log
BOTS='bot|crawl|spider|slurp|facebookexternalhit|headless|python-requests|curl/|wget|ahrefs|semrush|mj12|dotbot|petalbot|bytespider|censys|zgrab'
total=$(wc -l < $LOG); bots=$(grep -icE "$BOTS" $LOG)
echo "bots: $bots / $total = $(( 100 * bots / total ))%"

و برای حذفشان از هر تحلیلی، کافی است grep -viE "$BOTS" را جلوی دستورهای بخش دوم بگذارید.

اما فیلتر بر اساس نام مرورگر فقط ربات‌های صادق را می‌گیرد. ربات‌های بدخواه خودشان را کروم جا می‌زنند. برای آن‌ها به نشانه‌های رفتاری نگاه کنید — مثلاً این یک‌خطی که آی‌پی‌هایی را پیدا می‌کند که ده‌ها صفحه گرفته‌اند ولی حتی یک فایل CSS یا JS نخواسته‌اند (مرورگر واقعی چنین چیزی ندارد):

awk '$7 ~ /\.(css|js)($|\?)/ {asset[$1] = 1}
     {seen[$1]++}
     END {for (i in seen) if (!(i in asset) && seen[i] > 20) print seen[i], i}' \
    /var/log/nginx/access.log | sort -rn | head
💡 نکته: بین «حذف از آمار» و «مسدودکردن» خط قرمز بکشید؛ بلاک‌کردن Googlebot یعنی خداحافظی با سئو، فقط از گزارش کنارش بگذارید. در سمت مقابل هم زیاده‌روی نکنید: فیلترکردن هر درخواست بدون ارجاع‌دهنده، تایپ مستقیم آدرس و مرورگرهای حریم‌خصوصی‌محور را هم قربانی می‌کند.

سه منبع دیگر آلودگی آمار که همه فراموش می‌کنند: آی‌پی خودتان و همکارانتان، مانیتور آپتایم که هر دقیقه صفحه‌ی اصلی را می‌زند، و کرون‌های خود سایت که با curl به خودش درخواست می‌دهد. هر سه را با یک grep -v ساده کنار بگذارید.

نگه‌داری داده: پنجره‌ی کوتاه، خودش کنترل حریم خصوصی است

لاگ دسترسی شما فقط یک فایل متنی نیست؛ ترکیب آی‌پی، زمان دقیق و مشخصات مرورگر عملاً داده‌ی شخصی است و می‌شود با آن رفتار افراد را بازسازی کرد. توصیه‌ی عملی و ساده: لاگ خام را بیشتر از دو روز نگه ندارید و به‌جایش عددهای تجمیع‌شده را برای همیشه ذخیره کنید — «۳۴۰۲ بازدید در ۸ مرداد» را می‌شود سال‌ها نگه داشت و به هیچ آدمی اشاره نمی‌کند.

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    rotate 2
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data adm
}

و اگر می‌خواهید آی‌پی از همان لحظه‌ی نوشتن ناقص ثبت شود، در Nginx:

map $remote_addr $ip_anon {
    ~^(?<a>\d+\.\d+\.\d+)\.\d+$   "$a.0";
    default                        "0.0.0.0";
}
log_format privacy '$ip_anon - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log privacy;

چرا پنجره‌ی کوتاه خودش یک کنترل امنیتی است و نه یک ژست؟ چون ریسک نشت مستقیماً با حجم تاریخچه بالا می‌رود؛ اگر فردا سرورتان در دسترس کسی قرار بگیرد، «دو روز لاگ» و «دو سال لاگ» تفاوت یک حادثه‌ی کوچک با یک فاجعه است. داده‌ای که ندارید، نه لو می‌رود و نه باید از آن محافظت کنید. ضمناً لاگ چرخش‌نخورده یکی از رایج‌ترین دلایل پرشدن دیسک روی سرورهای کوچک است — موضوعی که در راهنمای مانیتورینگ سرور لینوکس به آن پرداخته‌ایم.

سؤالات پرتکرار

چرا آمار گوگل آنالیتیکس با آمار سرور من فرق دارد؟

چون دو چیز متفاوت را می‌شمارند: لاگ سرور همه‌ی درخواست‌ها را ثبت می‌کند (شامل ربات‌ها و فایل‌های استاتیک) ولی آنالیتیکس فقط مرورگرهایی را که جاوااسکریپت اجرا کرده‌اند. اختلاف ۲۰ تا ۴۰ درصدی عادی است؛ اگر اختلاف چند برابری دیدید، دارید Hit را با Page view مقایسه می‌کنید.

آمار بازدید بدون گوگل آنالیتیکس ممکن است؟

بله و دقیق‌تر هم هست. GoAccess روی لاگ خود سرور کار می‌کند، اسکریپتی به صفحه اضافه نمی‌کند، داده‌ای به بیرون نمی‌فرستد و ادبلاک روی آن اثری ندارد. اگر تحلیل رفتار هم می‌خواهید، Umami را کنارش بگذارید.

چرا در آمارم چند آی‌پی هزاران بازدید دارند؟

تقریباً همیشه به این دلیل که سایت پشت کلادفلر یا یک پراکسی است و آن آی‌پی‌ها سرورهای لبه‌اند، نه بازدیدکننده. با تنظیم real_ip_header در Nginx یا mod_remoteip در Apache این مشکل حل می‌شود. اگر بعد از تنظیم درست هم یک آی‌پی هزاران درخواست داشت، آن‌وقت واقعاً با ربات یا اسکنر طرفید.

آیا می‌توانم بدون کوکی بازدیدکننده‌ی یکتا بشمارم؟

می‌توانید، ولی فقط در بازه‌ی یک‌روزه و به‌صورت تخمینی: از هش آی‌پی و مرورگر با یک نمک که هر ۲۴ ساعت عوض می‌شود. در ایران به‌خاطر CGNAT و تعویض آی‌پی موبایل، این تخمین خطای قابل‌توجهی دارد؛ کوکی فرست‌پارتی همچنان دقیق‌ترین گزینه است.

نگه‌داشتن لاگ چقدر جا می‌گیرد؟

هر خط لاگ حدود ۲۰۰ تا ۳۰۰ بایت است؛ سایتی با روزانه ۱۵۰ هزار درخواست، حدود ۴۰ مگابایت لاگ خام در روز تولید می‌کند که فشرده‌شده به ۴ مگابایت می‌رسد. با نگه‌داری دو روزه عملاً هیچ فشاری روی دیسک نیست.

قدم بعدی

ترتیب کار روشن است: اگر پشت CDN هستید اول آی‌پی واقعی را درست کنید، بعد یک شب با awk لاگ خام را بخوانید تا شکل واقعی ترافیکتان دستتان بیاید، بعد GoAccess را برای گزارش دائمی بگذارید و فقط در صورت نیاز به تحلیل رفتار سراغ Umami بروید. همان اول logrotate را هم روی دو روز تنظیم کنید.

اگر می‌خواهید این‌ها را بدون ریسک تمرین کنید — کلادفلر را جلوی یک دامنه‌ی تستی بگذارید، هدر را جعل کنید و ببینید کانفیگ درست چطور جلویش را می‌گیرد — یک سرور ابری ساعتی بسازید و فقط هزینه‌ی همان چند ساعت را بدهید.

آموزش‌های مرتبط

آماده‌ی تمرین عملی هستید؟

سرور ابری ساعتی مهران هاست در ۶۰ ثانیه تحویل می‌شود — تمرین کنید و فقط بابت همان ساعت‌ها پرداخت کنید. هزینه را پیش از ثبت‌نام با محاسبه‌گر برآورد کنید.