Minimal serverless contact backend

A simple, serverless contact backend using Cloudflare Workers and KV, designed for reliability, clarity, and full operator control.

Project created on: 25 Nov. 2025 | Last updated: 26 Nov. 2025

This project is a deliberately simple contact backend built to avoid third-party form services, frameworks, and unnecessary complexity.

Messages are submitted via a Cloudflare Worker, validated and normalized, and stored directly in KV as structured JSON. There is no encryption beyond transport security, and the operator is explicitly trusted with message contents.

The goal was not privacy by cryptography, but operational clarity: understanding exactly how data flows, where it is stored, and how administrative access is controlled.

Overview

Problem

Hosted form services introduce external dependencies, opaque storage, pricing limits, and long-term availability risks for a simple requirement: receiving messages.

Solution

A small Cloudflare Worker that validates submissions, normalizes timestamps, and stores messages in KV, paired with a private admin view for reading entries.

Core Stack

Cloudflare Workers · KV Storage · One-time admin URLs · Server-rendered HTML · No frameworks

Architecture

The architecture is intentionally straightforward: accept input, normalize data, store records, and render results. There are no sessions, accounts, or background processes.

Key Features

Minimal by design

Eliminates sessions, accounts, and frameworks to reduce moving parts.

UTC normalization

Every entry is stored in YYYY-MM-DD HH:MM:SS UTC, guaranteeing consistent ordering.

Anti-spam honeypot

Invisible field + rate limiting blocks automated submissions without CAPTCHAs.

KV storage model

Durable, globally replicated, schema-free structured storage.

Admin renderer

Server-rendered sorting table with no external dependencies.

Screenshots

Sample code snippet

This snippet shows how each message is normalized to UTC and safely stored in KV:

// Normalize current time to UTC
const now = new Date();
const utc = now.toISOString().replace("T", " ").replace("Z", " UTC");

// Store structured message in KV
await MESSAGES.put(id, JSON.stringify({
  name,
  email,
  message,
  ip,
  client_tz,
  client_time,
  utc_time: utc
}));
      

Possible future improvements

Links

GitHub: https://github.com/grayguava/contactform-backend/

Live Demo: No Live demo or deployments available