Logo

输入您的 GA4 Property ID(纯数字)

Published on
...
Authors

背景

作为一个托管在 Cloudflare Pages 上的 Hugo 静态博客,我一直想在文章页面显示真实的浏览量数据。但问题在于:

  1. Google Analytics 的 gtag.js 只能"写"数据,不能"读"历史数据
  2. 静态博客没有后端,无法直接调用 GA4 Data API
  3. 不想依赖第三方服务(如不蒜子),希望完全掌控数据

经过调研,我选择了 Cloudflare Workers + Google Analytics Data API 的方案,完美解决了这个问题。


最终效果

文章页面元信息中会显示浏览量(日期、阅读时间、作者后面):

2025-11-04 · 5 分钟 · geekhuashan · 👁 123 次浏览

特性:

  • ✅ 显示 GA4 的真实全局浏览量
  • ✅ 5 分钟双层缓存(Workers + 前端)
  • ✅ 支持 1k+、10k+ 格式化
  • ✅ 失败降级,不影响页面加载
  • ✅ 完全免费(Cloudflare Workers 免费额度完全够用)

技术架构

访客浏览器
    ↓ 请求文章页面
前端 JavaScript (page-views.js)
1. 先显示缓存 (localStorage, 5 分钟)
2. 异步请求 API
Cloudflare Workers (pageviews.js)
1. 检查 Cache API (5 分钟)
2. 使用服务账号认证
3. 调用 GA4 Data API
Google Analytics 4
    ↓ 返回真实浏览量
前端显示:👁 123 次浏览

实现步骤

第 1 步:创建 Google Cloud 服务账号

1.1 启用 GA4 Data API

访问 Google Cloud Console,在 APIs & ServicesLibrary 中搜索并启用:

Google Analytics Data API

1.2 创建服务账号

进入 IAM & AdminService Accounts,创建新服务账号:

  • Service account name: ga4-pageviews-reader
  • Role: 不需要 GCP 项目权限,跳过

下载 JSON 密钥文件(妥善保管,不要上传到 GitHub)。

1.3 在 GA4 中授权

登录 Google Analytics

  1. 进入 AdminProperty access management
  2. 点击 + Add users
  3. 输入服务账号邮箱(如 [email protected]
  4. 角色选择:Viewer(查看者)
  5. 取消勾选 "Notify new users by email"

1.4 获取 GA4 Property ID

在 GA4 管理页面,进入 Property settings,复制 Property ID(纯数字,如 123456789)。


第 2 步:创建 Cloudflare Workers

2.1 Workers 代码结构

创建 workers/pageviews.js

export default {
  async fetch(request, env) **
    // 1. 处理 CORS
    if (request.method === 'OPTIONS') **
      return handleCORS(request);
    **

    // 2. 获取路径参数
    const url = new URL(request.url);
    const path = url.searchParams.get('path');

    // 3. 检查缓存
    const cached = await getCachedData(path, env);
    if (cached) return jsonResponse(cached, 200, request, true);

    // 4. 从 GA4 API 获取数据
    const pageViews = await fetchGA4PageViews(path, env);

    const data = **
      path: path,
      views: pageViews,
      updatedAt: new Date().toISOString(),
      cached: false
    **;

    // 5. 缓存并返回
    await setCachedData(path, data, env);
    return jsonResponse(data, 200, request);
  **
};

核心功能

  • 使用 Google Cloud 服务账号认证(JWT + RS256 签名)
  • 调用 GA4 Data API 查询指定页面的 screenPageViews
  • 使用 Cache API 缓存 5 分钟
  • CORS 白名单保护

2.2 配置文件 wrangler.toml

name = "geekhuashan-pageviews"
main = "pageviews.js"
compatibility_date = "2024-01-01"

第 3 步:部署 Workers

3.1 安装 Wrangler CLI

npm install -g wrangler

3.2 登录 Cloudflare

cd workers
wrangler login

浏览器会自动打开授权页面,点击 Allow

3.3 设置环境变量(Secrets)

设置 GA4 Property ID

wrangler secret put GA4_PROPERTY_ID

设置 Google Cloud 凭证

cat your-service-account.json | jq -c . | wrangler secret put GA4_CREDENTIALS

⚠️ 重要:Secrets 是加密存储在 Cloudflare 中的,不会泄露到 Git 仓库。

3.4 部署

wrangler deploy

成功后会显示 Workers URL:

Deployed geekhuashan-pageviews
   https://geekhuashan-pageviews.xxx.workers.dev

第 4 步:前端集成

4.1 HTML 结构(Hugo 模板)

修改 layouts/partials/post_meta.html,在元信息中添加浏览量占位符:

<span class="page-views" data-page-url="{{ .RelPermalink }}">
  <span class="view-icon">👁</span>
  <span class="view-count">0</span> 次浏览
</span>

4.2 JavaScript 调用 API

创建 assets/js/page-views.js

(function() {
    'use strict';

    const API_ENDPOINT = 'https://your-worker.workers.dev';
    const CACHE_DURATION = 5 * 60 * 1000; // 5 分钟

    function initPageViews() {
        const viewContainer = document.querySelector('.page-views');
        if (!viewContainer) return;

        const pageUrl = viewContainer.getAttribute('data-page-url');
        const viewCountElement = viewContainer.querySelector('.view-count');

        // 1. 先显示缓存
        const cachedData = getCachedViewCount(pageUrl);
        if (cachedData && cachedData.views !== undefined) **
            viewCountElement.textContent = formatViewCount(cachedData.views);
        **

        // 2. 异步获取最新数据
        fetchPageViews(pageUrl)
            .then(function(data) {
                if (data && data.views !== undefined) **
                    viewCountElement.textContent = formatViewCount(data.views);
                    setCachedViewCount(pageUrl, data);
                **
            })
            .catch(function(error) {
                console.warn('获取浏览量失败:', error);
                if (!cachedData) **
                    viewCountElement.textContent = '-';
                **
            });
    }

    async function fetchPageViews(pageUrl) **
        const url = `$**API_ENDPOINT**?path=$**encodeURIComponent(pageUrl)**`;
        const response = await fetch(url);
        if (!response.ok) throw new Error(`API 请求失败: $**response.status**`);
        return await response.json();
    **

    function formatViewCount(count) **
        if (count >= 10000) **
            return Math.floor(count / 1000) + 'k+';
        ** else if (count >= 1000) **
            return (count / 1000).toFixed(1).replace('.0', '') + 'k+';
        ** else **
            return count.toString();
        **
    **

    // 缓存函数省略...

    // DOM 加载完成后初始化
    if (document.readyState === 'loading') **
        document.addEventListener('DOMContentLoaded', initPageViews);
    ** else **
        initPageViews();
    **
})();

4.3 CSS 样式

创建 assets/css/extended/page-views.css

.page-views **
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--secondary);
    white-space: nowrap;
**

.page-views .view-icon **
    font-size: 0.95em;
    opacity: 0.8;
**

.page-views .view-count **
    font-weight: 500;
    color: var(--primary);
**

@media print **
    .page-views **
        display: none !important;
    **
**

测试验证

本地测试

hugo server -D

open http://localhost:1313/your-article/

打开浏览器开发者工具(F12),查看:

  1. Network 标签页,找到 pageviews?path= 请求
  2. 检查响应格式:
**
  "path": "/your-article/",
  "views": 123,
  "updatedAt": "2025-11-04T07:22:54.892Z",
  "cached": false
**

直接测试 Workers API

curl "https://your-worker.workers.dev?path=/test/"

性能优化

双层缓存策略

  1. Workers Cache API(服务端):

    • 缓存时长:5 分钟
    • 作用:减少 GA4 API 调用次数
    • 位置:Cloudflare 边缘节点
  2. localStorage(客户端):

    • 缓存时长:5 分钟
    • 作用:减少 Workers API 请求
    • 位置:访客浏览器

成本分析

Cloudflare Workers 免费计划

  • ✅ 100,000 请求/天
  • ✅ 10ms CPU 时间/请求
  • ✅ 无限制出站流量

实际消耗估算(假设日均 1000 PV):

  • 1000 PV ÷ 5 分钟缓存 = 约 200 请求/天
  • 远低于免费额度

Google Analytics Data API

  • ✅ 完全免费
  • ✅ 无请求次数限制

总成本:$0 🎉


安全考虑

已实现的安全措施

  1. 服务账号最小权限:仅授予 GA4 查看者(Viewer)权限
  2. Secrets 加密存储:敏感信息通过 wrangler secret 加密保存
  3. CORS 白名单:仅允许指定域名访问 API
  4. 速率限制:通过缓存机制防止滥用

敏感信息保护

⚠️ 绝对不要将以下内容提交到 Git

  • JSON 密钥文件(*.json
  • GA4 Property ID
  • 服务账号邮箱

workers/.gitignore 中添加:

*.json
!package.json
!wrangler.toml
.env
*.key

可能遇到的问题

问题 1:API 返回 500 错误

原因:GA4 API 调用失败

排查步骤

  1. 检查 GA4_PROPERTY_ID 是否正确(纯数字)
  2. 检查服务账号是否已在 GA4 中授权(Viewer 权限)
  3. 使用 wrangler tail 查看实时日志

问题 2:CORS 错误

原因:Workers 中的 CORS 白名单未包含您的域名

解决:修改 pageviews.js 中的 ALLOWED_ORIGINS

const ALLOWED_ORIGINS = [
  'https://geekhuashan.com',
  'http://localhost:1313'
];

重新部署:wrangler deploy

问题 3:浏览量显示为 0

原因:GA4 中该页面路径无数据

排查

  1. 登录 Google Analytics,检查路径格式是否正确
  2. GA4 数据有 24-48 小时延迟,新文章可能暂时无数据
  3. 确认 gtag.js 是否正确加载并发送 page_view 事件

扩展优化

1. 绑定自定义域名

在 Cloudflare Dashboard 配置 Workers Route:

  • Route: api.geekhuashan.com/pageviews*
  • Worker: geekhuashan-pageviews

更新前端 API 端点:

const API_ENDPOINT = 'https://api.geekhuashan.com/pageviews';

2. 显示其他指标

修改 Workers 中的 GA4 API 查询,可以获取:

  • 访客数(activeUsers
  • 停留时间(averageSessionDuration
  • 跳出率(bounceRate

3. 使用 KV 存储

将 Cache API 升级为 Cloudflare KV,获得更稳定的缓存:

# wrangler.toml
[kv_namespaces](/kv_namespaces)
binding = "PAGEVIEWS_KV"
id = "your-kv-namespace-id"

总结

通过 Cloudflare Workers + Google Analytics Data API 的方案,我成功为静态博客添加了真实的浏览量统计功能,核心优势:

完全免费:Cloudflare 和 GA4 的免费额度完全够用 ✅ 性能优秀:双层缓存 + 边缘计算,全球低延迟 ✅ 数据准确:直接从 GA4 获取真实数据 ✅ 完全掌控:不依赖第三方服务,代码和数据都在自己手中 ✅ 易于维护:Serverless 架构,无需管理服务器

如果您也在使用 Hugo + Cloudflare Pages,强烈推荐尝试这个方案!


参考资源


项目源码

完整代码已开源在 GitHub(敏感信息已移除):

# 文件结构
workers/
├── pageviews.js       # Workers 主函数
├── wrangler.toml      # 部署配置
└── package.json       # npm 依赖

themes/your-theme/
├── assets/
│   ├── js/page-views.js              # 前端脚本
│   └── css/extended/page-views.css   # 样式
└── layouts/partials/
    ├── post_meta.html                # 显示位置
    └── extend_footer.html            # 脚本加载

欢迎 Star 和 Fork!⭐


2024-11-04 更新:文章首发,欢迎交流讨论!

输入您的 GA4 Property ID(纯数字) | 原子比特之间