This guide explains how to enable HTTPS/TLS encryption for GraphDone in both development and production environments.
-
Generate development certificates:
./scripts/generate-dev-certs.sh
-
Enable SSL in environment:
# Copy .env.example to .env if you haven't already cp .env.example .env # Edit .env file SSL_ENABLED=true SSL_KEY_PATH=./deployment/certs/server-key.pem SSL_CERT_PATH=./deployment/certs/server-cert.pem HTTPS_PORT=4128 # Update client URLs for HTTPS VITE_GRAPHQL_URL=https://localhost:4128/graphql VITE_GRAPHQL_WS_URL=wss://localhost:4128/graphql
-
Start the development server:
npm run dev
-
Access the application:
- API: https://localhost:4128/graphql
- Web: https://localhost:3127 (configure web server for HTTPS separately)
- Health: https://localhost:4128/health
| Variable | Description | Default | Required |
|---|---|---|---|
SSL_ENABLED |
Enable/disable TLS | false |
No |
SSL_KEY_PATH |
Path to private key file | - | Yes (when SSL enabled) |
SSL_CERT_PATH |
Path to certificate file | - | Yes (when SSL enabled) |
HTTPS_PORT |
HTTPS server port | 4128 |
No |
SSL_ENABLED=true
SSL_KEY_PATH=./deployment/certs/server-key.pem
SSL_CERT_PATH=./deployment/certs/server-cert.pem
HTTPS_PORT=4128SSL_ENABLED=true
SSL_KEY_PATH=/etc/ssl/private/graphdone.key
SSL_CERT_PATH=/etc/ssl/certs/graphdone.crt
HTTPS_PORT=443Use the provided HTTPS Docker configuration:
# Generate certificates first
./scripts/generate-dev-certs.sh
# Start with HTTPS configuration
cd deployment
docker compose -f docker-compose.yml up-
Obtain production certificates (Let's Encrypt, CA, etc.)
-
Place certificates in deployment/certs:
deployment/ ├── certs/ │ ├── server-key.pem # Private key │ └── server-cert.pem # Certificate └── docker-compose.yml -
Deploy:
cd deployment docker compose -f docker-compose.yml up -d
For production, use certificates from a trusted Certificate Authority:
-
Let's Encrypt (Free):
certbot certonly --webroot -w /var/www/html -d yourdomain.com cp /etc/letsencrypt/live/yourdomain.com/privkey.pem ./certs/server-key.pem cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem ./certs/server-cert.pem
-
Commercial CA: Follow your CA's instructions for certificate generation
- Use strong cipher suites
- Enable HTTP Strict Transport Security (HSTS)
- Disable insecure protocols (SSLv3, TLS 1.0, TLS 1.1)
- Regular certificate renewal
- Secure private key storage (restricted permissions)
For production deployments, consider using a reverse proxy like Nginx or Caddy:
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
location / {
proxy_pass http://localhost:4127;
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;
}
location /graphql {
proxy_pass http://localhost:4127/graphql;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
}
}yourdomain.com {
reverse_proxy localhost:4127
}When HTTPS is enabled, WebSocket connections automatically upgrade to WSS:
- HTTP mode:
ws://localhost:4127/graphql - HTTPS mode:
wss://localhost:4128/graphql
The client automatically detects the protocol and uses the appropriate WebSocket URL.
-
Test HTTP health endpoint:
curl http://localhost:4127/health
-
Test HTTPS health endpoint:
curl -k https://localhost:4128/health
-
Test GraphQL over HTTPS:
curl -k -X POST -H "Content-Type: application/json" \ -d '{"query":"query { __schema { queryType { name } } }"}' \ https://localhost:4128/graphql
Run the TLS test suite:
# Run TLS-specific unit tests
npm test -- tls.test.ts
# Run comprehensive TLS integration tests
./scripts/test-tls.sh
# Run E2E tests including TLS scenarios
npm run test:e2e -- tls-integration.spec.ts-
"SSL_KEY_PATH environment variable is required"
- Ensure SSL_KEY_PATH and SSL_CERT_PATH are set when SSL_ENABLED=true
-
"SSL key file not found"
- Check file paths and permissions
- Generate certificates:
./scripts/generate-dev-certs.sh
-
"Certificate appears to be invalid"
- Verify certificate format (PEM)
- Check certificate expiry:
openssl x509 -in cert.pem -enddate -noout
-
Browser security warnings (development only)
- Expected with self-signed certificates
- Click "Advanced" → "Proceed to localhost" in browser
- For production, use CA-signed certificates
-
WebSocket connection fails over HTTPS
- Ensure client uses
wss://instead ofws:// - Check that VITE_GRAPHQL_WS_URL uses wss protocol
- Ensure client uses
# Check certificate validity
openssl x509 -in deployment/certs/server-cert.pem -text -noout
# Check private key validity
openssl rsa -in deployment/certs/server-key.pem -check -noout
# Test server response
curl -v -k https://localhost:4128/health
# Check if server is listening on HTTPS port
netstat -an | grep 4128After setting up HTTPS, verify everything is working:
# 1. Verify certificates exist
ls -la deployment/certs/server-*.pem
# Should show both server-key.pem and server-cert.pem
# 2. Start the server with HTTPS
npm run dev
# 3. Test HTTPS endpoints
curl -k https://localhost:4128/health
curl -k https://localhost:4128/graphql -d '{"query":"{ __typename }"}'
# 4. Run TLS integration tests
npm run test:e2e -- tests/e2e/tls-integration.spec.ts| Problem | Cause | Solution |
|---|---|---|
| "SSL key file not found" | Wrong certificate path | Use ./deployment/certs/server-*.pem paths |
| Tests skip with "TLS tests require certificates" | Missing dev certificates | Run ./scripts/generate-dev-certs.sh |
| "ENOENT: no such file 'generate-dev-certs.sh'" | Script reference issue | Use symlink: ln -sf manage-certificates.sh scripts/generate-dev-certs.sh |
| Browser shows certificate warnings | Self-signed certificate | Expected for development - click "Proceed" |
| Playwright tests fail with certificate errors | Missing ignoreHTTPSErrors | Add ignoreHTTPSErrors: true to playwright.config.ts |
The GraphDone server supports both HTTP and HTTPS modes through conditional server creation:
- HTTP mode: Uses Node.js
http.createServer() - HTTPS mode: Uses Node.js
https.createServer()with SSL certificates - Graceful fallback: Automatically falls back to HTTP if SSL configuration fails
GraphDone-Core/
├── packages/server/src/
│ └── config/
│ ├── tls.ts # TLS configuration module
│ └── tls.test.ts # TLS unit tests
├── scripts/
│ ├── generate-dev-certs.sh # Development certificate generator
│ └── test-tls.sh # TLS validation script
├── deployment/
│ ├── docker-compose.yml # Standard HTTP Docker config
│ └── docker-compose.yml # HTTPS Docker config
├── tests/e2e/
│ └── tls-integration.spec.ts # E2E TLS tests
└── deployment/certs/ # Generated certificates (dev certs gitignored)
├── server-key.pem
└── server-cert.pem
- Certificate validation: Automatic validation of SSL certificates and keys
- Protocol detection: Client automatically detects HTTPS and uses WSS for WebSockets
- Error handling: Graceful degradation when TLS configuration fails
- Development safety: Self-signed certificates for local development only
- Production deployment: Set up CA-signed certificates
- Monitoring: Implement SSL certificate expiry monitoring
- Performance: Consider TLS termination at load balancer level
- Security: Implement HSTS and security headers