iToverDose/Software· 27 MAY 2026 · 12:02

Simplify Astro blog comments using Netlify Forms without a backend

Discover how to add a secure, self-hosted comment system to your static Astro blog using only Netlify Forms and serverless functions—no databases or third-party scripts required.

DEV Community4 min read0 Comments

Static site generators like Astro deliver blazing-fast performance, but integrating user comments without heavy infrastructure can feel daunting. Traditional solutions—Disqus, Commento, or even custom backends—introduce complexity, external dependencies, or unwanted scripts. A simpler path exists for sites hosted on Netlify: leveraging Netlify Forms and lightweight serverless functions to build a fully controlled, low-maintenance comment system.

This approach mirrors the minimalist philosophy of Jamstack—using tools you already rely on to solve a common need. The result? A comment system that doesn’t compromise speed, security, or editorial control. Let’s walk through how it works and how to implement it on your own Astro blog.

A four-step workflow for secure comment approval

The system relies on Netlify’s built-in form handling and webhooks to automate comment moderation without manual backend setup. The process unfolds in four steps:

  • A visitor submits a comment via a standard HTML form. Netlify captures the POST request and stores it in a queue labeled blog-comments, bypassing your origin server entirely.
  • A webhook fires immediately after submission, triggering a serverless function called comment-handler. This function generates an email containing two secure, time-limited links—one to approve the comment, another to delete it.
  • When you click Approve, the link calls approve-comment, which copies the data into a second form named approved-comments and removes it from the queue. The original submission date is preserved for accurate display.
  • A third function, get-comments, fetches only from the approved-comments form. Readers never see unapproved content, and no raw email addresses or personal data are exposed in the browser.

This design eliminates databases, dashboards, and third-party scripts. Comments go live only after explicit approval from your inbox—and the entire flow runs on Netlify’s infrastructure.

Building the comment form in Astro

Netlify Forms works by scanning static HTML for forms marked with data-netlify="true". In an Astro project, this means using a server-rendered .astro component so the form appears in the final HTML and is registered automatically on first deploy.

---
const postId = Astro.url.pathname.split('/').pop();
---

<form name="blog-comments" method="POST" data-netlify="true" netlify-honeypot="bot-field">
  <input type="hidden" name="form-name" value="blog-comments" />
  <input type="hidden" name="postSlug" value={postId} />
  
  <!-- Honeypot field for spam protection -->
  <input name="bot-field" style="display:none" tabindex="-1" />
  
  <!-- Visible fields -->
  <label>
    Name:
    <input type="text" name="name" required />
  </label>
  
  <label>
    Email (optional):
    <input type="email" name="email" />
  </label>
  
  <label>
    Comment:
    <textarea name="comment" required></textarea>
  </label>
  
  <button type="submit">Submit</button>
</form>

Key points in the form structure:

  • The form-name field ensures Netlify routes submissions correctly, especially when using JavaScript-based submission via fetch.
  • The postSlug field associates each comment with a specific post. It’s used later to filter comments by URL path.
  • The hidden bot-field acts as a honeypot. Bots typically fill every field, while real users ignore it. Netlify silently discards submissions that trigger it.

The form submits via fetch with Content-Type: application/x-www-form-urlencoded, and Netlify intercepts these requests before they reach your server.

Serverless functions: handling, approving, and retrieving comments

Three lightweight Netlify Functions work together to process, moderate, and serve comments.

Function 1: get-comments – fetching approved comments

The get-comments function reads from the approved-comments form using the Netlify API and filters results by postSlug. It ensures privacy by hashing email addresses server-side before sending them to the frontend.

// netlify/functions/get-comments.js
import crypto from "node:crypto";

function gravatarHash(email) {
  return crypto
    .createHash("md5")
    .update(email.trim().toLowerCase())
    .digest("hex");
}

const comments = submissions
  .filter((s) => s.data?.postSlug === slug)
  .map((s) => ({
    name: s.data.name,
    comment: s.data.comment,
    date: s.data.originalDate || s.created_at,
    emailHash: s.data.email ? gravatarHash(s.data.email) : null,
  }))
  .sort((a, b) => new Date(a.date) - new Date(b.date));

The function uses originalDate instead of created_at to preserve the timestamp when the visitor submitted the comment, not when it was approved. The originalDate value is stamped during the approval step. All secrets live in environment variables, and the function returns an empty array in local development to prevent errors.

Function 2: comment-handler – triggering moderation emails

When a new submission lands in blog-comments, a Netlify outgoing webhook triggers comment-handler, which sends a moderation email via Resend. Each email includes two HMAC-SHA256-signed action links—one for approval, one for deletion.

// netlify/functions/comment-handler.js
import crypto from "node:crypto";

function hmac(submissionId, action, secret) {
  return crypto
    .createHmac("sha256", secret)
    .update(`${submissionId}:${action}`)
    .digest("hex");
}

const approveToken = hmac(id, "approve", secret);
const deleteToken = hmac(id, "delete", secret);

Each token encodes the submission ID and intended action. This prevents token reuse across submissions and actions, enhancing security. The links point to the approve-comment function and include the token as a query parameter.

Function 3: approve-comment – re-posting and archiving

The approve-comment function validates the HMAC token using crypto.timingSafeEqual to prevent timing attacks, then re-submits the comment data to the approved-comments form via Netlify’s API. It also sets originalDate to the original submission timestamp before the comment is marked as approved.

With these three functions in place, your Astro blog gains a fully functional comment system that stays fast, private, and under your control—all without a database or external service.

Looking ahead: a sustainable comment model for static sites

Static sites prioritize speed and simplicity, but user engagement often demands comments. This Netlify-based approach delivers both: a near-zero-maintenance comment system that respects performance and editorial autonomy.

As Jamstack ecosystems evolve, tools like Netlify Forms and serverless functions continue to blur the line between static and dynamic capabilities. For bloggers and small teams, that means more features without the overhead. The system outlined here can scale from personal blogs to small communities—all while keeping your site lean, fast, and secure.

AI summary

Learn how to implement a self-hosted comment system for Astro using only Netlify Forms and serverless functions—no database, no external scripts, and full control.

Comments

00
LEAVE A COMMENT
ID #GNW8OG

0 / 1200 CHARACTERS

Human check

4 + 8 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.