How the Ghost Comments System Works: Architecture, Features, and Moderation
Ghost implements a full-stack comments feature using a three-layer architecture of service, controller, and API endpoints, with moderation capabilities including bulk actions, reporting, and status management accessible through both member and admin contexts.
The Ghost comments system is built into the core of the TryGhost/Ghost repository as a native feature rather than an external plugin. It provides a complete solution for member engagement with sophisticated content gating, email notifications, and administrative moderation tools.
Core Architecture of the Ghost Comments System
Ghost organizes its comments functionality into three distinct layers that separate business logic from API presentation.
Service Layer
The CommentsService class in ghost/core/core/server/services/comments/comments-service.js contains the core business logic. This service handles data access, permission checks, email notifications, and all CRUD operations. Key methods include commentOnPost (line 309), replyToComment (line 667), and bulkUpdateStatus (lines 506-516). The service also dispatches domain events like MemberCommentEvent to allow other Ghost subsystems to react to comment activity.
Controller Layer
The CommentsController in ghost/core/core/server/services/comments/comments-controller.js translates API frames into service calls. It handles request validation, extracts member context from authentication frames, and manages impersonation for admin routes. The controller also sets cache-invalidation headers (X-Cache-Invalidate) via handleCacheHeaders to ensure CDN cache consistency after mutations.
Endpoint Layer
The API routes are declared in ghost/core/core/server/api/endpoints/comments.js, which maps HTTP endpoints such as /comments, /comments/browse, and /comments/report to controller methods. This file wires the public-facing routes to the underlying controller logic.
Enabling and Access Control
Comments are gated by the comments_enabled setting, which the service checks via the checkEnabled() method (lines 56-61). This setting supports three values:
- off – All comment actions are blocked; the service throws a
MethodNotAllowedError - all – Any signed-in member may comment
- paid – Only members with active paid subscriptions may comment
All public comment actions—including commentOnPost, replyToComment, and likeComment—invoke this guard before proceeding.
Content Gating for Paid Members
When comments_enabled is set to paid, the service performs additional validation through checkCommentAccess (lines 66-70) to verify the member's subscription status. The contentGating.checkPostAccess call (lines 74-80) also enforces any paywall restrictions on the target post itself, ensuring comments respect the post's visibility settings.
Comment Lifecycle and CRUD Operations
Creating Comments and Replies
When a member posts a comment, the flow proceeds through three stages:
- Controller validation: The
addmethod incomments-controller.jsvalidates the payload and forwards it to the service viacommentsService.api.commentOnPostorreplyToComment - Service processing: The service fetches member and post models, checks access permissions, builds the comment payload, and persists it via
models.Comment.add - Post-persistence: The service triggers
sendNewCommentNotificationsand dispatches aMemberCommentEventfor analytics
The system differentiates between top-level comments (commentOnPost) and threaded replies (replyToComment).
Reading and Browsing Comments
For public consumption, CommentsController.browse builds filters for specific post_id values and delegates to service.getComments, which uses models.Comment.findPage (lines 74-78). For reply threads, CommentsController.replies invokes service.getReplies, which also leverages findPage with parentId filtering.
Editing and Deleting Comments
Administrators and members can modify comments through specific service methods:
deleteComment(lines 452-506): Changes the comment status todeletedrather than physical removaleditCommentContent(lines 452-506): Updates the HTML content and sets theedited_attimestamp to track modifications
Comment Moderation in Ghost
The Ghost comments system provides comprehensive moderation capabilities through the admin API, accessible even when the global comments_enabled setting is off.
Admin Browse and Filtering
The adminBrowseAll controller method calls service.getAdminAllComments (lines 202-215) to return paginated lists of all comments with optional nested replies (includeNested parameter). This allows moderators to view the complete comment history regardless of post or status filters applied to public endpoints.
Reporting and Managing Reports
Members can report inappropriate content through the reportComment method (lines 147-169), which:
- Persists a
CommentReportrecord in the database - Triggers an email notification to staff via
notifyReport
Administrators can view who reported a specific comment using getCommentReporters (lines 240-258) and see engagement metrics through getCommentLikes (lines 262-280), which returns the member list of users who liked a comment.
Bulk Moderation Actions
For efficient content management, the service provides bulkUpdateStatus (lines 506-516), which accepts an NQL filter and a target status. This allows administrators to hide, publish, or delete multiple comments simultaneously based on criteria such as member ID or date ranges.
Cache Invalidation
All mutation endpoints in the controller set X-Cache-Invalidate headers to trigger CDN purge events. This ensures that cached comment fragments on the frontend are immediately cleared when content is moderated or updated.
Email Notifications and Events
The comments system integrates with Ghost's email infrastructure through CommentsServiceEmails (comments-service-emails.js). New comments trigger notifications to:
- Post authors via
notifyPostAuthors - Parent comment authors (for replies) via
notifyParentCommentAuthor - Staff members when reports are filed via
notifyReport
These emails are rendered using templates from comments-service-email-renderer.js.
Permission Model and Impersonation
Public comment routes require an authenticated member context, extracted from options.context.member.id via the checkMember method.
Because the admin UI operates on a different subdomain, the controller supports impersonation through the impersonate_member_uuid parameter (lines 28-34). This allows administrators to perform actions like liking comments on behalf of specific members while respecting member-specific logic and permissions.
Summary
The Ghost comments system provides a production-ready engagement platform with the following capabilities:
- Three-tier architecture separating service logic, API controllers, and HTTP endpoints for maintainability
- Flexible access control supporting fully open, member-only, or paid-subscriber-only commenting
- Complete moderation tooling including bulk updates, reporting workflows, and audit trails with
edited_attracking - Integrated email notifications for authors, reply recipients, and staff alerts
- Cache-aware mutations with automatic CDN invalidation through
X-Cache-Invalidateheaders - Admin impersonation support for cross-domain management actions
Frequently Asked Questions
How do I enable comments on a Ghost site?
Comments are controlled by the comments_enabled setting in your Ghost admin panel. Set it to all to allow any member to comment, paid to restrict commenting to paid subscribers only, or off to disable the feature entirely. According to the source code in comments-service.js, changing this setting immediately affects all comment endpoints without requiring a restart.
Can administrators edit or delete member comments?
Yes. The CommentsService provides editCommentContent and deleteComment methods that administrators can access through the admin API. The deleteComment method performs a "soft delete" by changing the status to deleted rather than removing the record, preserving the thread structure. Bulk operations are supported via bulkUpdateStatus using NQL filters to target specific comment sets.
How does Ghost handle inappropriate comment reports?
When a member reports a comment via the /comments/{id}/report/ endpoint, the reportComment method (lines 147-169) creates a CommentReport record and sends an email notification to staff. Administrators can then view the list of reporters through getCommentReporters and take moderation action using the standard bulk or individual status update tools.
What happens when a comment is posted on a paid-only post?
If comments_enabled is set to paid, the service executes checkCommentAccess to verify the member has an active paid subscription. Additionally, contentGating.checkPostAccess ensures the member has access to the post content itself before allowing the comment to be created. This dual-check prevents comment leakage on gated content.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →