- PHP 45%
- CSS 24.9%
- JavaScript 18%
- HTML 12.1%
| assets | ||
| data | ||
| .gitignore | ||
| .htaccess | ||
| admin.php | ||
| api.php | ||
| apple-touch-icon.png | ||
| favicon.ico | ||
| feed.atom.php | ||
| feed.json.php | ||
| feed.xml.php | ||
| index.html | ||
| LICENSE | ||
| og-image.png | ||
| opml.php | ||
| README.md | ||
| robots.txt | ||
| set-password.php | ||
| sitemap.php | ||
| sitemap.xml | ||
Pure Blog Discover
A community directory of blogs and websites built on the Pure Blog platform. Users can submit their blog for listing - the system automatically verifies whether the site uses Pure Blog and approves it instantly, without any manual review.
Built with PHP, vanilla JavaScript, and HTML/CSS. No frameworks, no databases - just flat JSON files.
Live: discover.thinkroot.xyz
Features
- Automatic verification - the server fetches the submitted URL and uses a multi-signal scoring system to detect Pure Blog
- Instant approval - if Pure Blog is detected, the blog is added to the directory immediately
- Language detection - automatically detects the blog's language from the
<html lang>attribute - Favicon fetching - displays each blog's favicon via Google Favicon Service
- Search - client-side search by blog name or URL
- Pagination - 20 blogs per page
- Admin panel - password-protected panel to add, edit, and remove blogs
- Rate limiting - max 3 submissions per IP per hour (bypassed in admin)
- SEO ready - meta tags, Open Graph, Twitter Card, JSON-LD structured data, dynamic sitemap
- Security hardened - CSRF protection, SSRF prevention, honeypot, security headers via
.htaccess - Open source - MIT License
File structure
pureblog-discover/
├── index.html # Public-facing directory page
├── api.php # REST API - list, submit, admin endpoints
├── admin.php # Password-protected admin panel
├── sitemap.php # Dynamic XML sitemap (generated from blogs.json)
├── set-password.php # CLI utility to generate admin password hash
├── robots.txt # Search engine directives
├── .htaccess # Apache config - security headers, caching, gzip
├── favicon.ico # Multi-size favicon (16/32/48px) - must stay in root
├── apple-touch-icon.png # iOS home screen icon - must stay in root
├── LICENSE
├── README.md
├── assets/
│ ├── css/
│ │ ├── main.css # Public site styles
│ │ └── admin.css # Admin panel styles
│ ├── js/
│ │ ├── main.js # Public site JavaScript
│ │ └── admin.js # Admin panel JavaScript
│ └── images/
│ ├── logo.svg # Vector logo
│ ├── logo.png # Raster logo (2x)
│ ├── favicon.svg # Vector favicon
│ ├── favicon-16.png
│ ├── favicon-32.png
│ ├── favicon-48.png
│ ├── favicon-180.png
│ ├── og-image.svg # Open Graph image source (editable)
│ └── og-image.png # Open Graph image (1200x630)
└── data/
├── .htaccess # Denies all web access to this directory
├── blogs.json # Blog database
└── rate.json # Rate limit tracking (auto-managed)
Requirements
- PHP 7.4 or higher
- Apache with
mod_rewrite,mod_headers,mod_deflate,mod_expires - cURL enabled in PHP
- Write permissions on
data/blogs.jsonanddata/rate.json
Deploy on shared hosting
-
Upload all files to
public_html/(or a subdirectory) -
Set file permissions:
data/blogs.json → 644 data/rate.json → 644 data/ → 755 -
Set the admin password - run this command via SSH or a PHP terminal:
php set-password.phpCopy the generated hash and paste it into
admin.phpas the value ofADMIN_PASSWORD_HASH. -
Replace all occurrences of
YOUR_DOMAINwith your actual domain in:index.html- canonical URL, OG tagssitemap.php- BASE_URL constantrobots.txt- Sitemap directiveassets/images/og-image.svg- domain shown in the OG image (then regenerateog-image.png)
-
Verify the setup is working by visiting:
https://YOUR_DOMAIN/api.php?action=pingAll values should return
true. If something isfalse, the ping response will tell you exactly what needs to be fixed.
API endpoints
All endpoints are served by api.php.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
?action=list |
- | Returns all approved blogs as JSON |
GET |
?action=token |
- | Returns a CSRF token for the submit form |
GET |
?action=ping |
- | Diagnostic endpoint - checks PHP, cURL, file permissions |
POST |
?action=submit |
CSRF token | Public blog submission with rate limiting |
POST |
?action=admin-submit |
Admin session | Add a blog without rate limiting |
POST |
?action=admin-edit |
Admin session | Edit an existing blog's name, URL, or language |
POST |
?action=admin-delete |
Admin session | Remove a blog by ID |
Pure Blog detection
The system uses a multi-signal scoring approach with a threshold of 10 points. A blog is approved if the total score reaches 10.
| Signal | Points | Source |
|---|---|---|
String pure blog found in HTML |
10 | Homepage HTML |
<meta name="generator" content="Pure Blog"> |
10 | Homepage HTML |
<meta name="generator" content="Jekyll"> |
3 | Homepage HTML |
| Simple.css variables/references (2+) | 5 | Homepage HTML |
| Jekyll HTML class structure (3+) | 3 | Homepage HTML |
<generator>Jekyll</generator> in feed |
4 | /feed.xml, /atom.xml |
jekyll in sitemap |
3 | /sitemap.xml |
To ensure your blog is detected reliably, add this tag to your Jekyll template:
<meta name="generator" content="Pure Blog">
Admin panel
Access the admin panel at /admin.php. After logging in you can:
- Add a blog directly (no rate limit, full Pure Blog verification still applies)
- Edit a blog's name, URL, or language code inline
- Remove a blog (with a native browser confirmation dialog)
- Search the blog list by name or URL
Security
- CSRF protection - all public form submissions require a session-bound CSRF token
- SSRF prevention - submitted URLs are checked against private IP ranges before the server fetches them
- Honeypot - an invisible field traps automated bot submissions
- Rate limiting - public submissions are limited to 3 per IP per hour
- Security headers -
X-Frame-Options,X-Content-Type-Options,Content-Security-Policy,Referrer-Policy,Permissions-Policy - IP hashing - submitter IPs are stored as SHA-256 hashes, never in plain text
- Atomic writes -
blogs.jsonis written via temp file + rename to prevent data corruption - Data directory -
data/is protected by its own.htaccessthat denies all web access
Contributing
Contributions are welcome. To contribute:
- Fork the repository
- Create a branch:
git checkout -b feature/your-feature - Commit your changes:
git commit -m 'Add your feature' - Push to the branch:
git push origin feature/your-feature - Open a pull request
Please keep the spirit of the project: no frameworks, no databases, minimal dependencies.
License
MIT License - see LICENSE for full text.
Built with
This project was built with Claude AI by Anthropic.