This document covers security best practices and deployment guidelines for the FutureTracker backend API.
The backend implements multiple layers of security to protect against common web vulnerabilities:
- Rate Limiting: Prevents DoS attacks and API abuse
- Input Validation: Joi schemas validate all user input
- Security Headers: Helmet provides comprehensive HTTP security headers
- Audit Logging: All sensitive operations are logged
- Data Sanitization: Protection against NoSQL injection attacks
- Authentication: Clerk JWT-based authentication with user isolation
We use generous rate limits that allow normal usage patterns while preventing abuse:
- Limit: 2000 requests per 15 minutes per IP address
- Applies to: All
/api/*endpoints - Response: HTTP 429 with clear error message and retry time
- Limit: 1500 write operations (POST/PUT/PATCH/DELETE) per 15 minutes per IP address (shared across the same IP)
- Applies to: Opportunity creation, updates, and deletions
- Response: HTTP 429 with helpful tips for bulk operations
Rate Limit Error Response Example:
{
"error": "Write Rate Limit Exceeded",
"message": "You have made too many create/update/delete operations...",
"retryAfter": "2026-01-16T00:42:33.000Z",
"limit": 1500,
"window": "15 minutes",
"tip": "If you need to add many opportunities at once, you can still do so within this limit."
}All user input is validated using Joi schemas with the following rules:
Opportunity Fields:
title: Required (POST), 1-200 characters, trimmeddescription: Optional, max 2000 characterslink: Optional, valid URL (http/https), max 500 charactersdeadline: Optional, valid ISO date formatcategory: Optional, enum ['internship', 'hackathon']status: Optional, enum ['applied', 'interviewed', 'shortlisted', 'selected', 'rejected']notes: Optional, max 5000 characters
Validation Error Response Example:
{
"error": "Validation Error",
"message": "The request data is invalid",
"details": [
{
"field": "title",
"message": "Title is required and cannot be empty"
},
{
"field": "link",
"message": "Link must be a valid URL (http or https)"
}
]
}Enhanced Helmet configuration provides:
Content Security Policy (CSP):
- Restricts resource loading to same origin
- Allows inline styles (for React)
- Blocks object and frame embedding
- Allows HTTPS images
HTTP Strict Transport Security (HSTS):
- 1-year max age
- Includes subdomains
- Preload ready
Additional Headers:
- X-Frame-Options: DENY
- X-Content-Type-Options: nosniff
- X-XSS-Protection: enabled
- Referrer-Policy: strict-origin-when-cross-origin
All sensitive operations are logged in JSON format for monitoring and security analysis:
Request Logging (all write operations):
{
"timestamp": "2026-01-15T17:57:33.000Z",
"type": "REQUEST",
"method": "POST",
"path": "/api/opportunities",
"ip": "192.168.1.100"
}Action Logging (create/update/delete):
{
"timestamp": "2026-01-15T17:57:33.000Z",
"type": "AUDIT",
"action": "CREATE_OPPORTUNITY",
"userId": "user_abc123",
"resourceId": "550e8400-e29b-41d4-a716-446655440000",
"details": {
"title": "Software Engineering Internship",
"category": "internship"
}
}The backend includes HSTS headers, but you must configure SSL/TLS termination at the reverse proxy level.
Choose one of the following deployment scenarios:
Step 1: Install Nginx
# Ubuntu/Debian
sudo apt update
sudo apt install nginx
# macOS
brew install nginxStep 2: Install Certbot (for Let's Encrypt)
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
# macOS
brew install certbotStep 3: Obtain SSL Certificate
# Replace example.com with your domain
sudo certbot --nginx -d api.example.comStep 4: Configure Nginx
Create /etc/nginx/sites-available/futuretracker-api:
server {
listen 80;
server_name api.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
# SSL certificates (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security headers (additional layer)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}Step 5: Enable and Test
# Enable site
sudo ln -s /etc/nginx/sites-available/futuretracker-api /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Restart Nginx
sudo systemctl restart nginxStep 6: Auto-renewal
# Certbot automatically sets up auto-renewal. Test it:
sudo certbot renew --dry-runStep 1: Install Caddy
# Ubuntu/Debian
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
# macOS
brew install caddyStep 2: Configure Caddy
Create /etc/caddy/Caddyfile:
api.example.com {
reverse_proxy localhost:3001
}
Step 3: Start Caddy
# Ubuntu/Debian
sudo systemctl restart caddy
sudo systemctl enable caddy
# macOS
caddy startThat's it! Caddy automatically obtains and renews SSL certificates from Let's Encrypt.
Most cloud platforms handle SSL/TLS automatically:
Heroku:
- Deploy your app
- SSL is automatically enabled for all apps
- Ensure
trust proxyis set in Express (already configured)
Railway:
- Deploy your app
- Railway provides SSL automatically on their domains
- For custom domains, add your domain in settings
Render:
- Deploy your app
- SSL is automatic for all services
- For custom domains, add DNS records as instructed
AWS (EC2 + ALB):
- Deploy backend to EC2
- Create Application Load Balancer
- Attach ACM (AWS Certificate Manager) certificate to ALB
- Configure ALB to forward to EC2 instances
Create a .env file with the following:
# Server Configuration
NODE_ENV=production
PORT=3001
# CORS - Add your production frontend URL
CORS_ORIGIN=https://your-frontend-domain.com
# Clerk Authentication
CLERK_SECRET_KEY=your_clerk_secret_key_here
# Supabase
SUPABASE_URL=your_supabase_url_here
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key_hereSecurity Best Practices:
- Never commit
.envto version control - Use environment variable management tools (e.g., Doppler, AWS Secrets Manager)
- Rotate keys regularly
- Use different keys for staging and production
Using PM2 (Process Manager):
# Install PM2
npm install -g pm2
# Start application
cd backend
pm2 start src/server.js --name futuretracker-api
# Enable startup script
pm2 startup
pm2 save
# Monitor
pm2 status
pm2 logs futuretracker-apiUsing systemd (Linux):
Create /etc/systemd/system/futuretracker-api.service:
[Unit]
Description=FutureTracker API
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/futuretracker/backend
Environment=NODE_ENV=production
ExecStart=/usr/bin/node src/server.js
Restart=on-failure
[Install]
WantedBy=multi-user.target# Start service
sudo systemctl start futuretracker-api
sudo systemctl enable futuretracker-api
# Check status
sudo systemctl status futuretracker-apiBefore deploying to production, ensure:
- HTTPS is configured and working
- Environment variables are set correctly
-
.envis in.gitignore - CORS_ORIGIN is set to your frontend domain
- Supabase RLS policies are configured
- Clerk authentication is properly configured
- Rate limiting is tested and working
- Audit logs are being collected
- Server is behind a firewall (only ports 80/443 exposed)
- Regular backups are configured
- Monitoring and alerting are set up
View audit logs:
# If using PM2
pm2 logs futuretracker-api | grep AUDIT
# If using systemd
sudo journalctl -u futuretracker-api | grep AUDITMonitor rate limiting:
# Look for 429 responses
pm2 logs futuretracker-api | grep "Rate Limit"# Check for vulnerabilities
cd backend
npm audit
# Fix vulnerabilities
npm audit fix
# Update dependencies
npm update-
Auth Caching: In-memory cache resets on server restart
- Impact: Minimal - cache rebuilds automatically
- Mitigation: Use Redis in multi-server deployments
-
Token Revocation: No built-in token revocation handling
- Impact: Revoked tokens still work until expiry
- Mitigation: Clerk handles this at their level; tokens are short-lived
-
No API Versioning: Breaking changes will affect all clients
- Impact: Future updates may require frontend changes
- Mitigation: Plan API versions (e.g.,
/api/v1/,/api/v2/)
- Implement Redis for distributed auth caching
- Add API versioning (
/api/v1/) - Implement per-user rate limiting (not just per-IP)
- Add request signing for API key authentication
- Implement Web Application Firewall (WAF) rules
- Add DDoS protection (Cloudflare, AWS Shield)
- Implement anomaly detection for unusual access patterns
For security issues, please contact the maintainer directly rather than opening a public issue.
For general questions, refer to the main README and DOCUMENTATION.