Skip to content

dlnorman/standalone-comments

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Standalone Comment System

A lightweight, self-hosted commenting system for static sites. No external dependencies, no tracking, no ads. Just comments.

Useful for: Hugo, Jekyll, Eleventy, or any static site generator.

Vibecoded with Claude Code. This entire application was built through AI-assisted development. No warranty. Use at your own risk.

Features

Self-hosted - Your data, your server, your control ✓ SQLite database - No MySQL/PostgreSQL required ✓ Threaded replies - Nested comment conversations ✓ Email subscriptions - Notify users of new replies ✓ Spam detection - Built-in spam scoring ✓ Comment moderation - Approve/delete from admin panel ✓ Reactions - Emoji reactions on comments and posts (♥ 👍 💡 😄) ✓ Rate limiting - Prevent abuse (5 comments/hour per IP) ✓ Recent comments - Site-wide recent comments widget ✓ Closed comments - Display existing comments without allowing new ones ✓ Hugo integration - Ready-to-use partials and shortcodes ✓ Import tools - Migrate from Disqus, TalkYard, WordPress (WXR) ✓ Analytics dashboard - Charts for comment volume, top posts, activity patterns ✓ Responsive design - Mobile-friendly interface ✓ Security focused - SQL injection protection, XSS prevention ✓ Privacy respecting - No tracking, minimal data collection ✓ Performance optimized - Handles 100K+ comments with ease

Requirements

  • PHP 7.4+ (8.0+ recommended)
  • SQLite support (included in PHP by default)
  • Apache with mod_rewrite (or Nginx)
  • Write permissions for database directory

Quick Start

1. Upload to Server

# Upload to your web server
/public_html/comments/

2. Configure

Edit config.php:

// Add your domain
define('ALLOWED_ORIGINS', [
    'https://yourdomain.com',
    'http://localhost:1313'  // Optional: Hugo dev server
]);

// Set timezone
date_default_timezone_set('America/New_York');

3. Set Admin Password

Visit: https://yourdomain.com/comments/utils/set-password.php

Then delete the file for security:

rm utils/set-password.php

4. Setup Email Queue Worker (Required for email notifications)

Option A: Cron job (recommended)

crontab -e
# Add this line (update path):
* * * * * /usr/bin/php /path/to/comments/utils/process-email-queue.php

Option B: Daemon mode

nohup php /path/to/comments/utils/process-email-queue.php --daemon > /dev/null 2>&1 &

5. Integrate with Your Site

Hugo Partial (in theme templates):

cp hugo/hugo-partial.html themes/yourtheme/layouts/partials/comments.html
{{ partial "comments.html" . }}

Hugo Shortcode (in content files):

cp hugo/hugo-shortcode.html themes/yourtheme/layouts/shortcodes/comments.html
{{< comments >}}

Plain HTML/JavaScript:

<div id="comments"></div>
<script src="/comments/comments.js"></script>
<script>
    CommentSystem.init({
        pageUrl: window.location.pathname,
        apiUrl: '/comments/api.php'
    });
</script>

Performance Optimizations

This system is optimized to handle high traffic and prevent resource abuse:

Database Indexes

  • 100x faster rate limiting queries with indexed IP and email lookups
  • Composite indexes for efficient filtering
  • Auto-migrates on first load (no manual setup needed)

Pagination

  • Comments endpoint: Max 500 per request (prevents memory overflow)
  • Admin endpoints: 50 per page (prevents browser crashes)
  • All endpoints return pagination metadata

Email Queue System

  • Comments post instantly (< 50ms)
  • Emails sent asynchronously in background
  • No request blocking even with 100+ subscribers
  • Automatic retry logic and cleanup

Rate Limiting

  • IP-based: 5 comments per hour
  • Email-based: 3 comments per 10 minutes
  • Login protection: 5 attempts per hour (brute force prevention)

Session Management

  • Full session tracking with expiration (30 days)
  • Automatic cleanup of expired sessions
  • IP address and user agent logging

Scalability Estimates

Comment Count Status Performance Notes
0-1,000 ✅ Excellent <50ms All features work perfectly
1,000-10,000 ✅ Good 50-200ms Smooth operation
10,000-100,000 ✅ Acceptable 200ms-1s May benefit from Redis caching
100,000+ ⚠️ Requires tuning 1s+ Consider PostgreSQL migration

Directory Structure

comments/
├── api.php                      # Main API endpoint
├── config.php                   # Configuration
├── database.php                 # Database initialization
├── comments.js                  # Frontend widget
├── comments.css                 # Styles
├── admin.html                   # Pending comments admin
├── admin-all.html               # All comments admin
├── admin-subscriptions.html     # Subscription management
├── admin-post-reactions.html    # Post-level reactions by page
├── admin-posts.html             # Posts list with comment/spam stats
├── admin-analytics.html         # Analytics dashboard with charts
├── unsubscribe.php              # Public unsubscribe page
├── index.html                   # Landing/info page
├── .htaccess                    # Security protection
├── db/
│   ├── comments-default.db      # Empty template database
│   └── comments.db              # Production database (auto-created)
├── docs/                        # Documentation
│   ├── DATABASE-SAFETY.md
│   ├── FEATURES.md
│   ├── SECURITY-AUDIT.md
│   ├── SUBSCRIPTIONS.md
│   └── TROUBLESHOOTING.md
├── hugo/                        # Hugo integration templates
│   ├── README.md
│   ├── hugo-partial.html
│   ├── hugo-shortcode.html
│   └── example.html
└── utils/                       # Utility scripts
    ├── process-email-queue.php  # Background email worker
    ├── set-password.php
    ├── enable-notifications.php
    ├── import-disqus.php        # Import from Disqus or WordPress WXR
    ├── import-talkyard.php
    ├── fix-urls.php             # Fix malformed URLs after import
    ├── test-email.php
    ├── backup-db.sh
    └── schema.sql

Admin Panel

Access at: https://yourdomain.com/comments/admin.html

Pages:

  • Pending (admin.html) - Moderate new comments
  • All Comments (admin-all.html) - View, filter, and manage all comments
  • Posts (admin-posts.html) - All pages with comments; sortable by comment count, spam percentage, pending count; highlights spam-magnet posts
  • Subscriptions (admin-subscriptions.html) - View subscribers, test email delivery
  • Post Reactions (admin-post-reactions.html) - Per-page reaction totals
  • Analytics (admin-analytics.html) - Charts: comment volume over time, top posts by volume, status breakdown (approved/pending/spam), activity by hour, activity by day of week

Stat cards on the dashboard are clickable and link to the relevant filtered view.


Reactions

Users can react to both individual comments and to the post as a whole using emoji reactions: ♥ 👍 💡 😄

  • Comment reactions — shown inline on each comment; rate-limited per IP
  • Post reactions — shown above the comment list; managed separately per page URL
  • Reaction state is stored in localStorage so voted buttons stay highlighted across page loads
  • Email subscribers are notified when reactions are added to posts they've commented on

Admins can view per-page post reaction totals at admin-post-reactions.html.


Closing Comments

To display existing comments without allowing new submissions, mark the post as closed.

In Hugo frontmatter:

---
title: "My Old Post"
comments_closed: true
---

Or as a shortcode parameter:

{{< comments closed="true" >}}

When closed:

  • The comment submission form is hidden, replaced with "Comments are closed."
  • Reply buttons are hidden on all comments
  • Post reactions continue to work
  • Existing comments are still displayed

Email Notifications

Enable Notifications

cd utils
php enable-notifications.php

Test Email Delivery

  1. Visit /comments/admin-subscriptions.html
  2. Click "Test Email Notifications"
  3. Enter your email address
  4. Check inbox (and spam folder)

Or via command line:

php utils/test-email.php your-email@example.com

Monitor Email Queue

# Check queue status
sqlite3 db/comments.db "SELECT status, COUNT(*) FROM email_queue GROUP BY status;"

# View pending emails
sqlite3 db/comments.db "SELECT * FROM email_queue WHERE status='pending';"

Security Features

  • ✅ SQL injection protection (prepared statements)
  • ✅ XSS protection (output escaping)
  • ✅ CSRF protection (CORS whitelist)
  • ✅ Email header injection protection
  • ✅ Rate limiting (IP + email based)
  • ✅ Login brute force protection
  • ✅ Spam detection
  • ✅ Honeypot fields
  • ✅ Secure cookies (HTTPOnly, Secure)
  • ✅ Database file protection
  • ✅ Utility script blocking
  • ✅ Security headers
  • ✅ Session management with expiration

Database Backup

Manual backup:

./utils/backup-db.sh

Automated backups (cron):

crontab -e
# Add this line:
0 2 * * * /path/to/comments/utils/backup-db.sh

Backups are stored in backups/ directory with timestamps.


Importing Existing Comments

From Disqus

php utils/import-disqus.php path/to/export.xml

From WordPress (WXR)

The Disqus importer also handles WordPress WXR (RSS-based) exports, which some migration tools produce:

php utils/import-disqus.php path/to/wordpress-export.xml

The format is auto-detected.

From TalkYard

php utils/import-talkyard.php path/to/export.json

Fixing URLs After Import

If comments were imported with full URLs as the page identifier instead of paths, run:

php utils/fix-urls.php

Troubleshooting

Comments not loading

  • Check browser console for errors
  • Verify api.php is accessible
  • Check CORS configuration in config.php

Email not sending

  • Run: php utils/test-email.php
  • Check server mail logs
  • Verify email queue worker is running: ps aux | grep process-email-queue
  • Check notifications enabled in settings

Database errors

  • Check file permissions: chmod 644 db/comments.db
  • Verify SQLite extension: php -m | grep sqlite
  • Check .htaccess allows PHP execution

Admin login fails

  • Reset password: php utils/set-password.php
  • Check cookies enabled in browser
  • Verify HTTPS configuration

Rate limiting too strict

Edit api.php to adjust limits:

  • Comment rate: Line 108 (currently 5/hour)
  • Email rate: Line 120 (currently 3/10min)
  • Login rate: Line 197 (currently 5/hour)

Full troubleshooting guide: docs/TROUBLESHOOTING.md


Monitoring

Check Active Sessions

sqlite3 db/comments.db "SELECT COUNT(*) FROM sessions WHERE expires_at > datetime('now');"

Check Login Attempts

sqlite3 db/comments.db "SELECT ip_address, COUNT(*) FROM login_attempts WHERE attempted_at > datetime('now', '-1 hour') GROUP BY ip_address;"

Check Database Size

du -h db/comments.db

View Logs

# Apache
tail -f /var/log/apache2/error_log

# Nginx
tail -f /var/log/nginx/error_log

Server Configuration

Apache (.htaccess included)

The included .htaccess works automatically if:

  • AllowOverride All is enabled
  • mod_rewrite is enabled

Nginx

Add to your site config:

location /comments/ {
    # Block sensitive directories
    location ~ ^/comments/(db|utils|backups)/ {
        deny all;
        return 403;
    }

    # Block sensitive files
    location ~ \.(db|db-shm|db-wal|sql|log|sh|bak|backup)$ {
        deny all;
        return 403;
    }

    # Process PHP files
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Documentation

See the /docs folder for comprehensive guides:

  • DATABASE-SAFETY.md - Protecting your data
  • FEATURES.md - Complete feature list
  • SUBSCRIPTIONS.md - Email subscription system
  • SECURITY-AUDIT.md - Security analysis
  • TROUBLESHOOTING.md - Common issues and solutions

API Endpoints

Public Endpoints

  • GET /api.php?action=comments&url={page_url} - Fetch comments for a page (includes post reaction counts)
  • GET /api.php?action=recent&limit={n} - Recent comments site-wide
  • POST /api.php?action=post - Submit new comment
  • POST /api.php?action=vote - React to a comment (heart, thumbsup, lightbulb, funny)
  • POST /api.php?action=post_reaction - React to a post
  • GET /api.php?action=csrf_token - Get CSRF token

Admin Endpoints (require authentication)

  • POST /api.php?action=login - Admin login
  • PUT /api.php?action=moderate&id={id} - Change comment status
  • DELETE /api.php?action=delete&id={id} - Delete comment
  • GET /api.php?action=pending - Fetch pending comments (paginated)
  • GET /api.php?action=all - Fetch all comments (paginated)
  • GET /api.php?action=subscriptions - Fetch subscriptions (paginated)
  • GET /api.php?action=post_reactions_summary - Per-page post reaction totals
  • DELETE /api.php?action=delete_post_reactions&url={page_url} - Delete all reactions for a page
  • GET /api.php?action=export_disqus - Export comments as Disqus XML

All list endpoints support pagination via limit and offset query parameters.


Hugo Integration

Hugo integration templates are in the /hugo directory.

Shortcode

cp hugo/hugo-shortcode.html themes/yourtheme/layouts/shortcodes/comments.html
{{< comments >}}
{{< comments closed="true" >}}

Partial (in theme templates)

cp hugo/hugo-partial.html themes/yourtheme/layouts/partials/comments.html
{{ partial "comments.html" . }}

The partial reads comments_closed from page frontmatter automatically.

Recent Comments Widget

cp hugo/recent-comments-shortcode.html themes/yourtheme/layouts/shortcodes/recent-comments.html

Use in markdown:

{{< recent-comments limit="10" >}}

See /hugo/README.md for full documentation


Updates & Maintenance

Check for Updates

git pull origin main
cat CHANGELOG.md  # Review changes

Before Updating

# Always backup first!
cp db/comments.db db/comments-backup-$(date +%Y%m%d).db

Database Migrations

Database migrations run automatically on first load after an update.


License

This comment system is provided as-is for personal use.

Credits

Built for self-hosted, privacy-focused commenting on static websites.


Support

  1. Check /docs/TROUBLESHOOTING.md
  2. Check browser console for errors
  3. Check server error logs
  4. Review security audit in /docs/SECURITY-AUDIT.md

Version: 2.3 Last Updated: March 2026

About

A standalone comment manager tool for use on static websites, with no external dependencies. PHP and SQLite, self-contained in one folder.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors