A modernized custom website publisher that allows you to store and serve HTML websites via Val Town. Originally designed to work with Glif for AI-generated websites, but now supports any HTML content with password protection and public indexing!
β οΈ Important: All pages published to this service are public and visible to everyone on the index page. Use password protection only to prevent unauthorized updates, not for privacy.
- π Password Protection: Set passwords for pages to prevent unauthorized updates
- π Public Index: Browse all published pages in a beautiful interface
- ποΈ Modern Architecture: Restructured with proper backend/frontend separation
- πΎ SQLite Database: Metadata storage with proper schema management
- π Large HTML Support: Still handles very large HTML files (up to 10MB)
- π‘οΈ Enhanced Security: Better validation, password hashing, and error handling
- π Rich Metadata: Tracks creation/update times, file sizes, and protection status
- π¨ Beautiful UI: Modern, responsive interface with TailwindCSS
# POST HTML content directly curl -X POST "https://your-val-url.web.val.run/?key=my-site" \ -H "Content-Type: text/html" \ -d "<html><body><h1>Hello World!</h1></body></html>" # POST with password protection (for new pages) curl -X POST "https://your-val-url.web.val.run/?key=my-site&password=secret123" \ -H "Content-Type: text/html" \ -d "<html><body><h1>Protected Page!</h1></body></html>" # Update password-protected page curl -X POST "https://your-val-url.web.val.run/?key=my-site&password=secret123" \ -H "Content-Type: text/html" \ -d "<html><body><h1>Updated Content!</h1></body></html>" # POST JSON with HTML (legacy format) curl -X POST "https://your-val-url.web.val.run/?key=my-site" \ -H "Content-Type: application/json" \ -d '{"data": "<html><body><h1>Hello World!</h1></body></html>"}'
# View in browser https://your-val-url.web.val.run/?key=my-site # View index page (lists all public pages) https://your-val-url.web.val.run/ # Get JSON response with metadata https://your-val-url.web.val.run/?key=my-site&format=json
| Method | URL Pattern | Description |
|---|---|---|
GET | / | Public index page listing all published websites |
GET | /?key=<key> | View published website |
POST | /?key=<key> | Publish HTML content |
POST | /?key=<key>&password=<pass> | Publish with password protection |
GET | /?key=<key>&format=json | Get website data as JSON |
-
key (required for publish/view): Website identifier
- Format: Alphanumeric characters, hyphens, underscores only
- Max length: 100 characters
- Examples:
my-site,blog_2024,test-page-1
-
password (optional): Password for page protection
- Minimum length: 4 characters
- Can be passed as query parameter or
X-Passwordheader - Required for updating password-protected pages
- Only needed when creating new protected pages or updating existing ones
-
format (optional): Response format
html(default): Returns rendered HTMLjson: Returns JSON with metadata
Returns the published HTML with a small footer indicating it's hosted by valtownGeocities.
{ "data": { "html": "<html>...</html>", "createdAt": "2025-11-21T14:56:57.968Z", "updatedAt": "2025-11-21T14:56:57.968Z", "size": 2722 } }
{ "key": "my-site", "size": 2722, "createdAt": "2025-11-21T14:56:57.968Z", "updatedAt": "2025-11-21T14:56:57.968Z", "message": "Website published successfully", "passwordProtected": true }
- First Upload: When publishing to a new key with a password, the page becomes password-protected
- Subsequent Updates: All future updates to that key require the same password
- Public Viewing: Anyone can still view the published page (passwords only protect updates)
- No Recovery: If you forget the password, the page cannot be updated (by design)
- Passwords are hashed using SHA-256 with salt
- Passwords are never stored in plain text
- Minimum password length is 4 characters
- Use strong passwords for important pages
# Create password-protected page curl -X POST "https://your-val-url.web.val.run/?key=secret-page&password=mypassword123" \ -H "Content-Type: text/html" \ -d "<html><body><h1>This page is protected!</h1></body></html>" # Update the protected page (requires password) curl -X POST "https://your-val-url.web.val.run/?key=secret-page&password=mypassword123" \ -H "Content-Type: text/html" \ -d "<html><body><h1>Updated protected content!</h1></body></html>" # Try to update without password (will fail) curl -X POST "https://your-val-url.web.val.run/?key=secret-page" \ -H "Content-Type: text/html" \ -d "<html><body><h1>This will fail!</h1></body></html>" # Returns: 401 Unauthorized - Password required for this page
The index page (/) shows all published websites in a beautiful, responsive interface:
- Chronological Listing: Pages sorted by most recently updated
- Metadata Display: Shows creation date, update date, and file size
- Direct Links: Click any page title to view the website
- Search-Friendly: All pages are discoverable and indexable
- Responsive Design: Works great on mobile and desktop
- Large File Support: Handle HTML files up to 10MB
- Multiple Input Formats: Accept raw HTML or JSON with
datafield - Automatic Processing: Strips common formatting artifacts (like ```html blocks)
- Metadata Tracking: Automatic creation/update timestamps and size tracking
- Public Discovery: All pages listed on the index for easy browsing
- Password Protection: Optional password protection for page updates
- Key Validation: Secure key format validation prevents injection attacks
- Size Limits: Prevents abuse with 10MB file size limit
- Input Sanitization: Processes and cleans HTML input
- Secure Hashing: Passwords hashed with SHA-256 and salt
- Better Error Messages: Clear, actionable error responses
- HTTP Status Codes: Proper REST API status codes (400, 401, 413, 500, etc.)
- Legacy Support: Backward compatible with existing integrations
- Modern UI: Beautiful, responsive interface built with TailwindCSS
You can still use this with Glif for AI-generated websites:
- Generate: Run a Glif that generates HTML: https://glif.app/@jamiedubs/glifs/clqecfqdd000tc3vk4i208jl3
- Publish: The Glif will POST the generated HTML to your valtownGeocities instance
- Protect (optional): Add
&password=yourpasswordto the Glif's POST URL for protection - View: Access your website at:
https://your-val-url.web.val.run/?key=YOURKEYHERE
βββ backend/
β βββ index.ts # Main backend application (Hono)
βββ frontend/
β βββ index.html # Public index page interface
βββ shared/
β βββ types.ts # Shared TypeScript types
βββ main.tsx # Entry point (redirects to backend)
βββ README.md # This documentation
- Backend: Hono-based API server with SQLite database
- Frontend: Static HTML with vanilla JavaScript
- Storage: Val Town Blob storage for HTML content, SQLite for metadata
- Security: Password hashing, input validation, and secure key handling
# Test the modernized version curl -X POST "https://your-val-url.web.val.run/?key=test" \ -H "Content-Type: text/html" \ -d "<!DOCTYPE html><html><head><title>Test</title></head><body><h1>It works!</h1><p>Large HTML support β </p></body></html>" # View the result curl "https://your-val-url.web.val.run/?key=test" # Get JSON metadata curl "https://your-val-url.web.val.run/?key=test&format=json" # Create password-protected page curl -X POST "https://your-val-url.web.val.run/?key=protected&password=secret123" \ -H "Content-Type: text/html" \ -d "<!DOCTYPE html><html><head><title>Protected</title></head><body><h1>Protected Page</h1></body></html>"
[object Object]Display: Fixed the major bug where websites showed[object Object]instead of actual HTML content- Large File Handling: Improved handling of large HTML payloads
- Error Responses: Better error handling and user feedback
- JSON Processing: Fixed JSON input parsing issues
- Password Security: Secure password hashing and validation
- Database Schema: Proper SQLite schema with migrations
- Public Visibility: All published pages are visible on the public index
- Password Purpose: Passwords only protect against unauthorized updates, not viewing
- No Privacy: This is not a private hosting service - everything is public
- No Password Recovery: Lost passwords cannot be recovered (by design)
- Content Responsibility: You are responsible for the content you publish
This project is built on Val Town. To contribute:
- Fork the Val
- Make your changes
- Test thoroughly
- Submit a pull request or share your improvements
Open source and available for modification and redistribution. Built with β€οΈ on Val Town.