A self-hosted, secure contact form processing service for static websites. Enable functional contact forms on your static sites without backend code, with built-in spam protection and email forwarding.
- π Cloudflare Turnstile Integration - Bot protection with zero user friction
- π‘οΈ Authentication Bot Protection - Optional Turnstile protection for login/register pages
- β±οΈ Rate Limiting - IP-based request limiting to prevent abuse
- π§ Email Forwarding - Send form submissions directly to your inbox
- βͺοΈ Optional Callback Redirect - Redirect users to a custom URL after successful submission
- π₯οΈ Web Management UI - HTMX-based interface for easy form management
- π³ Docker Ready - Easy deployment with containerization
- πΎ SQLite Database - Simple, file-based persistence
- π JWT Authentication - Secure admin access
- π± Responsive Design - Mobile-friendly management interface
- Go 1.21+ (for development)
- Docker (for production deployment)
- Cloudflare Turnstile keys (get them here)
- SMTP credentials (Gmail, SendGrid, etc.)
# Create a directory for persistent data
mkdir -p /opt/staticsend/data
# Run with Docker
docker run -d \
--name staticsend \
-p 8080:8080 \
-v /opt/staticsend/data:/data \
-e STATICSEND_PORT=8080 \
-e STATICSEND_JWT_SECRET=your-super-secret-jwt-key \
-e STATICSEND_SMTP_HOST=smtp.gmail.com \
-e STATICSEND_SMTP_PORT=587 \
-e STATICSEND_SMTP_USER=your-email@gmail.com \
-e STATICSEND_SMTP_PASS=your-app-password \
-e TURNSTILE_PUBLIC_KEY=your-turnstile-public-key \
-e TURNSTILE_SECRET_KEY=your-turnstile-secret-key \
ghcr.io/CaffeinatedTech/staticsend:latest# Clone the repository
git clone https://github.com/CaffeinatedTech/staticsend.git
cd staticsend
# Build the binary
go build -o staticsend .
# Set environment variables
export STATICSEND_PORT=8080
export STATICSEND_DB_PATH=./staticsend.db
export STATICSEND_JWT_SECRET=your-jwt-secret
export STATICSEND_SMTP_HOST=smtp.gmail.com
export STATICSEND_SMTP_PORT=587
export STATICSEND_SMTP_USER=your-email@gmail.com
export STATICSEND_SMTP_PASS=your-app-password
export TURNSTILE_PUBLIC_KEY=your-turnstile-public-key
export TURNSTILE_SECRET_KEY=your-turnstile-secret-key
# Run the application
./staticsend
# Or with custom port
./staticsend -port=3000| Flag | Description | Default | Environment Variable |
|---|---|---|---|
-port |
HTTP server port | 8080 |
STATICSEND_PORT |
| Variable | Description | Default | Required |
|---|---|---|---|
PORT |
HTTP server port | 8080 |
No |
DATABASE_PATH |
SQLite database path | ./data/staticsend.db |
No |
JWT_SECRET_KEY |
JWT signing secret | - | Yes |
REGISTRATION_ENABLED |
Enable user registration | true |
No |
| Variable | Description | Default | Required |
|---|---|---|---|
EMAIL_HOST |
SMTP server host | - | Yes |
EMAIL_PORT |
SMTP server port | 587 |
No |
EMAIL_USERNAME |
SMTP username | - | Yes |
EMAIL_PASSWORD |
SMTP password | - | Yes |
EMAIL_FROM |
From email address | - | Yes |
EMAIL_USE_TLS |
Use TLS for SMTP | true |
No |
| Variable | Description | Default | Required |
|---|---|---|---|
TURNSTILE_PUBLIC_KEY |
Turnstile public key for login/register pages | - | No |
TURNSTILE_SECRET_KEY |
Turnstile secret key for login/register pages | - | No |
| Variable | Description | Default | Required |
|---|---|---|---|
S3_ENDPOINT |
S3-compatible storage endpoint | - | For backups |
S3_BUCKET |
S3 bucket name | - | For backups |
S3_ACCESS_KEY |
S3 access key | - | For backups |
S3_SECRET_KEY |
S3 secret key | - | For backups |
S3_REGION |
S3 region | us-east-1 |
No |
CLEANUP_OLD_BACKUPS |
Auto-delete backups older than 30 days | true |
No |
| Variable | Description | Default | Required |
|---|---|---|---|
CRONIVORE_CHECK_SLUG |
Cronivore check slug for backup monitoring | - | No |
CRONIVORE_URL |
Cronivore service URL | https://cronivore.com |
No |
- Access the web UI at
http://localhost:8080 - Register an account and log in
- Create a new contact form with:
- Form name and domain
- Cloudflare Turnstile keys (public and secret)
- Destination email address
- Optional Callback URL (redirect users here after a successful submission)
Add this HTML to your static website:
<form action="https://your-staticsend-instance.com/api/v1/submit/YOUR_FORM_KEY"
method="POST">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<textarea name="message" placeholder="Your Message" required></textarea>
<!-- Cloudflare Turnstile -->
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_PUBLIC_KEY"></div>
<button type="submit">Send Message</button>
</form>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>Form submissions will be:
- Validated by Cloudflare Turnstile
- Rate-limited by IP address
- Forwarded to your specified email address
- Stored in the database for review
If you configured a Callback URL on the form, the submitter will be redirected to that URL with an HTTP 303 See Other after a successful submission. If no Callback URL is configured, the API responds with a JSON payload indicating success (HTTP 201 Created).
POST /api/v1/submit/{form_key}
Content-Type: application/x-www-form-urlencoded
name=John&email=john@example.com&message=Hello&cf-turnstile-response=tokenResponse:
- 201 Created with JSON body when no Callback URL is configured on the form:
{
"success": true,
"message": "Form submitted successfully",
"submission_id": 123
}- 303 See Other redirect to the configured Callback URL when present.
POST /api/auth/register- User registrationPOST /api/auth/login- User loginGET /api/forms- List all formsPOST /api/forms- Create new form (fields:name,domain,turnstile_secret,forward_email,callback_url[optional])GET /api/forms/{id}- Get form detailsPUT /api/forms/{id}- Update form (fields:name,domain,turnstile_secret,forward_email,callback_url[optional])DELETE /api/forms/{id}- Delete formGET /api/submissions- List submissions (with optional form_id filter)
# Clone the repository
git clone https://github.com/CaffeinatedTech/staticsend.git
cd staticsend
# Install dependencies
go mod download
# Build
go build -o staticsend .
# Run tests
go test ./...
# Format code
go fmt ./...
# Run with hot reload (if using air)
airstaticsend/
βββ cmd/staticsend/ # Main application entry point
βββ pkg/ # Go packages
β βββ api/ # HTTP handlers and routing
β βββ auth/ # Authentication logic
β βββ database/ # Database operations
β βββ email/ # Email sending service
β βββ middleware/ # HTTP middleware
β βββ models/ # Data models
β βββ turnstile/ # Cloudflare Turnstile integration
β βββ utils/ # Utility functions
βββ templates/ # HTML templates for HTMX UI
βββ migrations/ # Database migrations
βββ internal/ # Private application code
version: '3.8'
services:
staticsend:
image: ghcr.io/CaffeinatedTech/staticsend:latest
ports:
- "8080:8080"
volumes:
- staticsend_data:/app/data
environment:
- PORT=8080
- DATABASE_PATH=/app/data/staticsend.db
- JWT_SECRET_KEY=${JWT_SECRET}
- EMAIL_HOST=${EMAIL_HOST}
- EMAIL_PORT=${EMAIL_PORT}
- EMAIL_USERNAME=${EMAIL_USERNAME}
- EMAIL_PASSWORD=${EMAIL_PASSWORD}
- EMAIL_FROM=${EMAIL_FROM}
- TURNSTILE_PUBLIC_KEY=${TURNSTILE_PUBLIC_KEY}
- TURNSTILE_SECRET_KEY=${TURNSTILE_SECRET_KEY}
restart: unless-stopped
volumes:
staticsend_data:- Create a new service in Coolify using GitHub repository
- Select "Dockerfile" as build pack
- Configure environment variables (see Configuration section above)
- Add persistent volume:
/app/dataβ/var/lib/coolify/staticsend/data - Deploy with health check on
/healthendpoint
For detailed Coolify setup instructions, see docs/deployment/coolify-setup.md
StaticSend includes an automated backup system that uploads database backups to S3-compatible storage.
- SQLite database backup using safe
.backupcommand - S3-compatible storage (AWS S3, DigitalOcean Spaces, Backblaze B2, etc.)
- Automatic compression and timestamping
- Old backup cleanup (configurable retention period)
- Cronivore monitoring integration for backup job monitoring
- Coolify cron job integration
-
Configure S3 environment variables in Coolify:
S3_ENDPOINT=https://s3.amazonaws.com S3_BUCKET=your-backup-bucket S3_ACCESS_KEY=your-access-key S3_SECRET_KEY=your-secret-key
-
Create Coolify cron job:
- Schedule:
0 2 * * *(daily at 2 AM) - Command:
/app/backup.sh
- Schedule:
-
Optional Cronivore monitoring:
CRONIVORE_CHECK_SLUG=your-check-slug
For complete backup setup instructions, see docs/deployment/backup-setup.md
We welcome contributions! Please feel free to submit issues, feature requests, and pull requests.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow Go coding standards
- Write tests for new functionality
- Update documentation for changes
- Use descriptive commit messages
This project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation
- π Issue Tracker
- π¬ Discussions
- Cloudflare Turnstile for bot protection
- Chi Router for HTTP routing
- HTMX for the lightweight frontend approach
- SQLite for simple data persistence
staticSend - Making static websites more interactive, one form at a time. π