# Building a Cloudflare-Native Comment System for Hugo

> I migrated my blog comments from Artalk to a custom Cloudflare-native stack using Workers, D1, and Telegram moderation, and now my entire blog infrastructure runs on Cloudflare.

Author: Vincent Yang
Published: 2026-03-01
Tags: Cloudflare, Workers, D1, Hugo, Comments, Artalk, Telegram
Source: https://missuo.me/posts/comment-system

---

I finally replaced my old comment setup and moved everything to a new system built by myself.

The result is simple: my blog is now fully running on Cloudflare.

- Static site: Hugo on Cloudflare Pages
- Image storage: Cloudflare R2
- CDN: Cloudflare
- Comment backend: Cloudflare Workers + D1

This post explains why I migrated from Artalk, what I built, and how the migration worked.

## Why I Migrated from Artalk

Artalk is a great project, and it worked well for me for a long time. I used it as a reference while redesigning my own system.

But I wanted tighter control over:

- data model and moderation flow
- deployment and operations
- integration with my own blog frontend
- Cloudflare-native infrastructure only

So instead of extending Artalk, I built a focused API-first comment service for my own use case.

## Architecture

The current architecture is intentionally minimal.

1. The blog frontend sends requests to a comment API endpoint.
2. Cloudflare Workers handles validation, moderation, and notification.
3. D1 stores pages, comments, threading metadata, and moderation state.
4. Telegram Bot receives new-comment notifications for moderation and admin replies.

The frontend is custom, integrated into my Hugo theme, and styled to keep the previous reading experience close to what I had before.

## Core Features

### 1) API-first comment service

No separate admin panel or heavy UI dependency in the backend. The Worker exposes clean endpoints for posting comments, listing comments, and stats.

### 2) Threaded comments with pagination

Each comment keeps `parent_id`, `root_id`, and `depth`, and public listing supports page-based pagination.

### 3) Flexible moderation

`AUTO_APPROVE` controls default behavior:

- `AUTO_APPROVE=false`: new comments are pending
- `AUTO_APPROVE=true`: new comments are approved immediately

In both modes, Telegram notifications are sent.

- Pending comments show `Approve / Spam / Delete`
- Auto-approved comments show `Spam / Delete`

### 4) Admin identity and anti-impersonation

Reserved admin emails are blocked from public submission, so regular users cannot pretend to be the site owner.

### 5) Gravatar and metadata

Avatar URLs are generated from email via Gravatar hash.

For network metadata, the system uses `api.ipinfo.es` and stores country, ASN, and related fields in D1. Country display uses full country name and a separate country code for flags.

### 6) Consistent timestamps

API timestamps are normalized to include timezone offsets, so old imported comments and new comments use a consistent format.

## Migration from Artalk

I migrated historical comments from an Artalk export file (`.artrans`) into D1.

High-level process:

1. Export data from the old Artalk instance.
2. Convert `.artrans` to D1-compatible SQL with a converter script.
3. Apply schema migrations on D1.
4. Import SQL into D1.
5. Run normalization migrations (owner identity, metadata fixes).
6. Verify thread structure, timestamps, and moderation state.

This gave me a smooth transition without losing old threads.

## Why This Stack Fits My Blog

I wanted one platform for everything.

Now the entire blog stack is inside Cloudflare:

- Pages for Hugo deployment
- R2 for objects
- CDN for global delivery
- Workers for compute logic
- D1 for relational comment data

No extra VM, no extra database vendor, no mixed ops stack.

## Final Notes

This system is built for my blog first, but the architecture is reusable.

The most important part for me is that I now control the full comment pipeline end to end: data model, moderation workflow, frontend behavior, and deployment.

Migrating from Artalk was straightforward once the converter and D1 migrations were in place.