This document outlines the security measures implemented to protect against mailbombing and other abuse of the contact form.
- Location:
/api/contact - Method: POST only
- Purpose: Centralized contact form processing with security checks
- Security Features:
- Email address hidden from frontend
- Server-side validation and sanitization
- Rate limiting enforcement
- Input validation (length, format, content)
- XSS prevention (HTML sanitization)
- Per IP Address: Tracks requests by client IP
- Hourly Limit: 5 requests per hour (configurable)
- Daily Limit: 20 requests per day (configurable)
- Response: Returns HTTP 429 with Retry-After header
- Memory: In-memory storage with automatic cleanup
Configuration (.env):
RATE_LIMIT_REQUESTS_PER_HOUR=5
RATE_LIMIT_REQUESTS_PER_DAY=20
- Email format validation (RFC-compliant)
- Required field validation (name, email, message)
- HTML entity encoding (prevents script injection)
- Length limits on all inputs (max 1000 chars)
- Minimum message length (5 characters)
- Character filtering for special characters
PUBLIC_CONTACT_EMAIL=jaimevillalcon@hotmail.com
FORMSUBMIT_EMAIL=jaimevillalcon@hotmail.com
RATE_LIMIT_REQUESTS_PER_HOUR=5
RATE_LIMIT_REQUESTS_PER_DAY=20
Important:
- Add
.envto.gitignore(keep sensitive data off git) - Update
.envon server manually or via secure deployment
- Updated to POST to
/api/contactinstead of FormSubmit directly - Added form clearing after successful submission
- Improved error message handling
- Better error feedback from server
- Astro Mode: Changed to
output: "server"for API route support - Process Manager: PM2 keeps Node.js app running
- Deploy Script: Updated to manage PM2 process lifecycle
User Browser
↓
Form Submit (POST /api/contact)
↓
Rate Limiter (check IP limits)
↓
Input Validation & Sanitization
↓
FormSubmit Service (hidden email)
↓
Your Mailbox (protected)
{
"success": true,
"message": "Email sent successfully",
"remaining": {
"hourly": 4,
"daily": 19
}
}{
"success": false,
"error": "Too many requests. Please try again later.",
"reason": "hourly_limit_exceeded"
}Header: Retry-After: 3600 (seconds)
{
"success": false,
"error": "Invalid email format"
}npm run dev
# Visit http://localhost:3000
# Test contact form at /api/contact# Make 6 rapid requests - 6th should fail with 429
curl -X POST http://localhost:3000/api/contact \
-H "Content-Type: application/json" \
-d '{"name":"Test","email":"test@test.com","message":"test message","phone":""}'- PM2 installed on server:
npm install -g pm2 - Node.js and npm available
./deploy.shThis will:
- Push code to git
- Pull latest on server
- Install dependencies
- Build with Astro (server mode)
- Start/restart Node.js app with PM2
- Restart nginx
Example nginx configuration to proxy requests:
server {
listen 80;
server_name jaimedigitalstudio.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Consider implementing:
- CAPTCHA Integration: Add reCAPTCHA v3 for bot detection
- Redis Rate Limiter: Replace in-memory with Redis for multi-instance scaling
- Email Verification: Confirm sender email address
- Spam Detection: Implement Akismet or similar service
- Request Logging: Log submissions for audit trail
- Webhook Notifications: Real-time alerts on contact form submission
pm2 status
pm2 logs jaimedigitalstudio
pm2 monitcurl http://jaimedigitalstudio.com/api/contact -X GET
# Should return 405 Method Not Allowed- ✅ Email hidden from source code
- ✅ Rate limiting prevents spam/mailbombing
- ✅ Input sanitization prevents injection attacks
- ✅ Server-side validation (cannot be bypassed)
- ✅ API endpoint not exposed in frontend
- ✅ Process manager ensures uptime
- Check PM2 logs:
pm2 logs jaimedigitalstudio - Verify .env file exists on server
- Check FORMSUBMIT_EMAIL is set correctly
- Edit
.envon server and adjust limits - Restart PM2:
pm2 restart jaimedigitalstudio
- Check browser console for errors
- Verify
/api/contactis accessible - Check nginx proxy configuration