Skip to content

Latest commit

 

History

History
482 lines (354 loc) · 11.3 KB

File metadata and controls

482 lines (354 loc) · 11.3 KB

Launchpage Contact Form Worker

Cloudflare Worker that handles form submissions from launchpage.xyz with honeypot protection and KV storage.

Features

  • Honeypot spam protection (winnie_pooh field)
  • Email validation
  • Input sanitization (XSS protection)
  • CORS support with configurable origins
  • Cloudflare KV storage
  • IP and geolocation tracking
  • Silent bot rejection (bots think submission succeeded)

Prerequisites

Initial Setup

1. Clone and Install Dependencies

git clone <repository-url>
cd launchpage-contact
npm install

2. Configure Cloudflare Account

First, login to Wrangler:

npx wrangler login

This will open a browser window to authenticate with your Cloudflare account.

3. Get Your Cloudflare Account ID

Via Wrangler CLI:

npx wrangler whoami

Via Cloudflare Dashboard:

  1. Go to https://dash.cloudflare.com/
  2. Select any domain (or Workers & Pages)
  3. Look in the right sidebar under "Account ID"
  4. Copy the Account ID

4. Create KV Namespaces

Create both production and preview namespaces:

# Production namespace
npx wrangler kv:namespace create "SUBMISSIONS"

# Preview namespace for development
npx wrangler kv:namespace create "SUBMISSIONS" --preview

You'll see output like this:

🌀 Creating namespace with title "launchpage-contact-SUBMISSIONS"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SUBMISSIONS", id = "abc123..." }

🌀 Creating namespace with title "launchpage-contact-SUBMISSIONS_preview"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SUBMISSIONS", preview_id = "xyz789..." }

Save these IDs - you'll need them in the next step.

5. Configure wrangler.toml

Copy the example configuration:

cp wrangler.example.toml wrangler.toml

Edit wrangler.toml and fill in your values:

account_id = "your-account-id-from-step-3"

[[kv_namespaces]]
binding = "SUBMISSIONS"
id = "your-production-kv-id-from-step-4"
preview_id = "your-preview-kv-id-from-step-4"

[vars]
ENVIRONMENT = "production"
# Add your allowed origins (comma-separated, wildcards supported)
ALLOWED_ORIGINS = "https://yourdomain.com,http://localhost:3000,https://*.netlify.app"

Important: The wrangler.toml file is gitignored to protect your sensitive IDs. Never commit this file to version control.

Development

Test Locally

Start the development server:

npm run dev

The worker will be available at http://localhost:8787

Test with Sample Requests

Valid submission:

curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -H "Origin: http://localhost:3000" \
  -d '{
    "firstName": "John",
    "email": "john@example.com",
    "page": "example.com",
    "winnie_pooh": ""
  }'

Bot submission (honeypot filled):

curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -H "Origin: http://localhost:3000" \
  -d '{
    "firstName": "Bot",
    "email": "bot@spam.com",
    "page": "example.com",
    "winnie_pooh": "https://spam.com"
  }'

The bot submission will return success but won't be stored in KV.

View Development Logs

npm run tail

Deployment

Option 1: Deploy via Wrangler CLI

Deploy to Cloudflare Workers:

npm run deploy

After deployment, you'll see output like:

Uploaded launchpage-contact (x.xx sec)
Published launchpage-contact (x.xx sec)
  https://launchpage-contact.your-subdomain.workers.dev

Your worker is now live!

Option 2: Deploy via Cloudflare Dashboard

  1. Go to Workers & Pages
  2. Click "Create Application" → "Create Worker"
  3. Name it launchpage-contact
  4. Click "Deploy"
  5. Go to "Settings" → "Variables and Secrets"
  6. Add environment variables:
    • ALLOWED_ORIGINS: Your allowed origins (comma-separated)
    • ENVIRONMENT: production
  7. Go to "Settings" → "Bindings"
  8. Add KV Namespace Binding:
    • Variable name: SUBMISSIONS
    • KV namespace: Select the one you created in step 4
  9. Click "Quick Edit" and paste your code from src/index.js
  10. Click "Save and Deploy"

Custom Domain Setup

Via Cloudflare Dashboard (Recommended)

  1. Go to Workers & Pages
  2. Click on your launchpage-contact worker
  3. Go to "Triggers" tab
  4. Click "Add Custom Domain"
  5. Enter your domain (e.g., contact.yourdomain.com)
  6. Click "Add Custom Domain"

Cloudflare will automatically:

  • Create DNS records
  • Issue SSL certificate
  • Route traffic to your worker

Via wrangler.toml

Uncomment and configure in wrangler.toml:

[route]
pattern = "contact.yourdomain.com"
custom_domain = true

Then redeploy:

npm run deploy

Frontend Integration

Update your form to submit to the worker:

<form id="contactForm">
  <input type="text" name="firstName" required>
  <input type="email" name="email" required>
  <input type="hidden" name="page" value="yourpage.com">

  <!-- Honeypot field (hidden from users) -->
  <input type="text" name="winnie_pooh" style="display:none" tabindex="-1" autocomplete="off">

  <button type="submit">Submit</button>
</form>

<script>
document.getElementById('contactForm').addEventListener('submit', async (e) => {
  e.preventDefault();

  const formData = {
    firstName: e.target.firstName.value,
    email: e.target.email.value,
    page: e.target.page.value,
    winnie_pooh: e.target.winnie_pooh.value
  };

  const response = await fetch('https://contact.yourdomain.com', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(formData)
  });

  const result = await response.json();
  console.log(result);
});
</script>

Managing Submissions

List All Submissions

npx wrangler kv:key list --binding=SUBMISSIONS

Get Specific Submission

npx wrangler kv:key get "submission:example.com:1704067200000" --binding=SUBMISSIONS

List Submissions by Domain

npx wrangler kv:key list --binding=SUBMISSIONS --prefix="submission:example.com:"

Delete a Submission

npx wrangler kv:key delete "submission:example.com:1704067200000" --binding=SUBMISSIONS

KV Storage Structure

Submissions are stored with the following key format:

submission:{page}:{timestamp}

Example: submission:example.com:1704067200000

Each entry contains:

{
  "firstName": "John",
  "email": "john@example.com",
  "page": "example.com",
  "submittedAt": "2024-01-01T00:00:00.000Z",
  "ip": "192.168.1.1",
  "userAgent": "Mozilla/5.0...",
  "country": "US"
}

Configuration

Environment Variables

Set in wrangler.toml under [vars]:

Variable Description Example
ENVIRONMENT Deployment environment production or development
ALLOWED_ORIGINS Comma-separated CORS origins (supports wildcards) https://yourdomain.com,https://*.netlify.app,http://localhost:3000

CORS Origins

The ALLOWED_ORIGINS variable supports:

  • Exact matches: https://yourdomain.com
  • Wildcards: https://*.netlify.app (matches any Netlify subdomain)
  • Multiple origins: Separate with commas

Monitoring

Real-time Logs

npm run tail

This shows live logs from your worker including:

  • Successful submissions
  • Bot detections
  • Errors

Cloudflare Dashboard

  1. Go to Workers & Pages
  2. Click on launchpage-contact
  3. View metrics:
    • Requests per second
    • Errors
    • CPU time
    • Invocation statuses

Security Features

  1. Honeypot Protection: The winnie_pooh field catches bots. If filled, submission is silently rejected (bot thinks it succeeded)
  2. Email Validation: Regex validation for proper email format
  3. Input Sanitization: Removes HTML tags (<>) and limits field length to 500 chars
  4. CORS Restrictions: Only allows requests from configured origins
  5. XSS Protection: All user input is sanitized before storage

Troubleshooting

CORS errors?

  • Verify your origin is in ALLOWED_ORIGINS in wrangler.toml
  • Check that the origin header matches exactly (including protocol)
  • Ensure wildcard patterns are correct (e.g., https://*.netlify.app)
  • Check browser console for specific CORS error messages

Submissions not showing up?

  • Verify KV namespace IDs in wrangler.toml are correct
  • Check logs: npm run tail
  • Confirm binding name is SUBMISSIONS in both code and config
  • Test locally first with npm run dev

Deployment fails?

  • Run npx wrangler login to re-authenticate
  • Verify account_id in wrangler.toml is correct
  • Ensure your Cloudflare account has Workers enabled
  • Check you're in the correct directory

"Error: No account_id found"

  • Make sure account_id is filled in wrangler.toml
  • Run npx wrangler whoami to verify authentication

KV namespace not found?

  • Verify the namespace IDs in wrangler.toml match those from wrangler kv:namespace create
  • Make sure you're using the correct namespace ID (not preview ID) for production

Cost Estimate

Cloudflare Workers Free Tier:

  • 100,000 requests/day
  • 10ms CPU time per request
  • KV: 100,000 reads/day, 1,000 writes/day
  • 1GB storage

For a typical launchpage:

  • Estimated: 10-100 submissions/day
  • Cost: $0/month (well within free tier)

If you exceed free tier:

  • Workers: $5/month for 10M requests
  • KV: $0.50/million reads, $5/million writes

Project Structure

launchpage-contact/
├── src/
│   └── index.js           # Main worker code
├── .gitignore             # Git ignore file (includes wrangler.toml)
├── package.json           # Dependencies and scripts
├── wrangler.example.toml  # Configuration template
├── wrangler.toml          # Your config (gitignored)
└── README.md              # This file

API Reference

POST /

Accepts form submissions.

Request:

{
  "firstName": "string (required)",
  "email": "string (required, valid email)",
  "page": "string (optional)",
  "winnie_pooh": "string (should be empty)"
}

Response (Success):

{
  "success": true,
  "message": "Thank you for your submission"
}

Response (Error):

{
  "error": "error_type",
  "message": "Human-readable error message"
}

Status Codes:

  • 200: Success
  • 400: Bad request (missing fields, invalid email)
  • 405: Method not allowed
  • 500: Internal server error

Future Enhancements

  • Email notifications via Resend/SendGrid
  • Duplicate email detection
  • Rate limiting per IP
  • Admin API to retrieve submissions
  • Webhook support for integrations (Slack, Discord, etc.)
  • CSV export functionality
  • Dashboard for viewing submissions

License

MIT

Support

For issues or questions:

  1. Check this README's troubleshooting section
  2. View Cloudflare Workers documentation
  3. Check Wrangler documentation