This guide explains how to configure TLS/HTTPS support in Bungate API Gateway for secure encrypted communications.
- Security Guide - Complete security hardening guide
- Authentication Guide - Secure your APIs with auth
- Quick Start - Get started with basic TLS setup
- API Reference - TLS configuration options
- Examples - Production TLS examples
- Overview
- Basic Configuration
- Advanced Configuration
- Configuration Options
- Default Cipher Suites
- Generating Self-Signed Certificates
- Production Best Practices
- HTTP to HTTPS Redirect
- TLS with JWT Key Rotation
- TLS Implementation Details
- Complete Production Example
- Environment Variables
- Monitoring and Logging
- Troubleshooting
- Security Considerations
- Examples
- Related Documentation
Bungate supports TLS/HTTPS with the following features:
- Certificate Management: Load certificates from files or buffers
- Cipher Suite Configuration: Control which cipher suites are allowed
- Protocol Version Enforcement: Set minimum TLS version (1.2 or 1.3)
- HTTP to HTTPS Redirect: Automatically redirect HTTP traffic to HTTPS
- Client Certificate Validation: Optional mutual TLS (mTLS) support
import { BunGateway } from 'bungate'
const gateway = new BunGateway({
server: {
port: 443,
},
security: {
tls: {
enabled: true,
cert: './path/to/cert.pem',
key: './path/to/key.pem',
},
},
})
await gateway.listen()const gateway = new BunGateway({
server: {
port: 443, // HTTPS port
},
security: {
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
redirectHTTP: true,
redirectPort: 80, // HTTP port for redirects
},
},
})const gateway = new BunGateway({
security: {
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
minVersion: 'TLSv1.3',
cipherSuites: [
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256',
],
},
},
})const gateway = new BunGateway({
security: {
tls: {
enabled: true,
cert: './server-cert.pem',
key: './server-key.pem',
ca: './ca-cert.pem',
requestCert: true,
rejectUnauthorized: true,
},
},
})import { readFileSync } from 'fs'
const gateway = new BunGateway({
security: {
tls: {
enabled: true,
cert: readFileSync('./cert.pem'),
key: readFileSync('./key.pem'),
},
},
})interface TLSConfig {
/** Enable TLS/HTTPS */
enabled: boolean
/** Certificate (file path or Buffer) */
cert?: string | Buffer
/** Private key (file path or Buffer) */
key?: string | Buffer
/** CA certificate for client validation (file path or Buffer) */
ca?: string | Buffer
/** Minimum TLS version */
minVersion?: 'TLSv1.2' | 'TLSv1.3'
/** Allowed cipher suites */
cipherSuites?: string[]
/** Request client certificates */
requestCert?: boolean
/** Reject unauthorized clients */
rejectUnauthorized?: boolean
/** Enable HTTP to HTTPS redirect */
redirectHTTP?: boolean
/** HTTP port for redirects */
redirectPort?: number
}Bungate uses secure cipher suites by default, prioritizing forward secrecy:
TLS 1.3 (Preferred):
TLS_AES_256_GCM_SHA384TLS_CHACHA20_POLY1305_SHA256TLS_AES_128_GCM_SHA256
TLS 1.2 (Fallback):
ECDHE-RSA-AES256-GCM-SHA384ECDHE-RSA-AES128-GCM-SHA256ECDHE-RSA-CHACHA20-POLY1305
For development and testing, you can generate self-signed certificates:
# Generate private key and certificate
openssl req -x509 -newkey rsa:4096 \
-keyout key.pem -out cert.pem \
-days 365 -nodes \
-subj "/CN=localhost"Warning: Self-signed certificates should never be used in production!
Obtain certificates from a trusted Certificate Authority (CA):
- Let's Encrypt (Free)
- Commercial CAs (DigiCert, GlobalSign, etc.)
security: {
tls: {
enabled: true,
minVersion: 'TLSv1.3',
// ...
},
}Avoid weak or deprecated cipher suites. Use the defaults or configure explicitly:
cipherSuites: ['TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256']security: {
tls: {
enabled: true,
// ...
},
securityHeaders: {
enabled: true,
hsts: {
maxAge: 31536000, // 1 year
includeSubDomains: true,
preload: true,
},
},
}Regularly rotate certificates before expiration:
- Monitor certificate expiration dates
- Automate renewal (e.g., with Let's Encrypt)
- Test certificate updates in staging first
- Store private keys securely (encrypted at rest)
- Use appropriate file permissions (600)
- Never commit keys to version control
- Consider using hardware security modules (HSM) for production
# Set secure permissions
chmod 600 key.pem
chmod 644 cert.pemWhen redirectHTTP is enabled, Bungate automatically starts a second server on the specified port that redirects all HTTP requests to HTTPS:
const gateway = new BunGateway({
server: { port: 443 },
security: {
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
redirectHTTP: true,
redirectPort: 80,
},
},
})The redirect server:
- Returns HTTP 301 (Moved Permanently)
- Preserves the request path and query parameters
- Sets the
Locationheader to the HTTPS URL - Closes the connection after redirect
Error: Failed to load certificate from ./cert.pem
Solution:
- Verify the file path is correct
- Check file permissions
- Ensure the certificate is in PEM format
Error: EADDRINUSE: address already in use
Solution:
- Check if another process is using the port
- Use a different port
- Stop the conflicting process
Error: TLS certificate validation failed
Solution:
- Ensure both cert and key are provided
- Verify the certificate and key match
- Check certificate expiration date
- Validate certificate format (PEM)
Error: SSL handshake failed
Solution:
- Check client TLS version compatibility
- Verify cipher suite compatibility
- Ensure certificate is trusted by client
- Check for certificate chain issues
The TLS configuration supports:
- PCI DSS requirements for encrypted transmission
- HIPAA security requirements
- GDPR data protection requirements
- SOC 2 security controls
Combine TLS/HTTPS with JWT key rotation for comprehensive security:
import { BunGateway } from 'bungate'
const gateway = new BunGateway({
server: {
port: 443,
},
security: {
// TLS configuration
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
minVersion: 'TLSv1.3',
redirectHTTP: true,
redirectPort: 80,
},
// JWT key rotation
jwtKeyRotation: {
secrets: [
{
key: process.env.JWT_SECRET_PRIMARY,
algorithm: 'HS256',
kid: 'primary-2024',
primary: true,
},
{
key: process.env.JWT_SECRET_OLD,
algorithm: 'HS256',
kid: 'old-2023',
deprecated: true,
},
],
gracePeriod: 7 * 24 * 60 * 60 * 1000, // 7 days
},
},
routes: [
{
pattern: '/api/*',
target: 'http://backend:3000',
auth: {
secret: process.env.JWT_SECRET_PRIMARY,
jwtOptions: {
algorithms: ['HS256'],
},
},
},
],
})
await gateway.listen()For dynamic key rotation using JWKS:
const gateway = new BunGateway({
security: {
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
minVersion: 'TLSv1.3',
},
jwtKeyRotation: {
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
jwksRefreshInterval: 3600000, // 1 hour
secrets: [
{
key: process.env.JWT_FALLBACK_SECRET,
algorithm: 'HS256',
kid: 'fallback',
},
],
},
},
})Support different JWT algorithms simultaneously:
const gateway = new BunGateway({
security: {
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
},
jwtKeyRotation: {
secrets: [
{
key: rsaPublicKey,
algorithm: 'RS256',
kid: 'rsa-key-1',
primary: true,
},
{
key: 'hmac-secret',
algorithm: 'HS256',
kid: 'hmac-key-1',
},
{
key: ecPublicKey,
algorithm: 'ES256',
kid: 'ec-key-1',
},
],
},
},
})- Add New Key: Add new key as primary
- Grace Period: Keep old key for verification
- Monitor Usage: Track deprecated key usage
- Remove Old Key: After grace period expires
// Step 1: Add new key (both keys active)
jwtKeyRotation: {
secrets: [
{
key: 'new-secret',
algorithm: 'HS256',
kid: 'key-2024-02',
primary: true,
},
{
key: 'old-secret',
algorithm: 'HS256',
kid: 'key-2024-01',
deprecated: true,
},
],
gracePeriod: 7 * 24 * 60 * 60 * 1000, // 7 days
}
// Step 2: Monitor for 7 days
// Logs warnings when old key is used
// Step 3: Remove old key (after grace period)
jwtKeyRotation: {
secrets: [
{
key: 'new-secret',
algorithm: 'HS256',
kid: 'key-2024-02',
primary: true,
},
],
}The TLS manager automatically loads certificates from files or buffers:
// From files
tls: {
enabled: true,
cert: './path/to/cert.pem',
key: './path/to/key.pem',
ca: './path/to/ca.pem', // Optional
}
// From buffers
import { readFileSync } from 'fs'
tls: {
enabled: true,
cert: readFileSync('./cert.pem'),
key: readFileSync('./key.pem'),
}The gateway validates certificates on startup:
- Checks that both cert and key are provided
- Validates file paths and permissions
- Ensures certificates are in PEM format
- Verifies certificate and key match
- Checks certificate expiration (warning only)
When redirectHTTP is enabled, a separate HTTP server is started:
// Automatic redirect server
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
redirectHTTP: true,
redirectPort: 80,
}The redirect server:
- Listens on the specified port (default: 80)
- Returns HTTP 301 (Moved Permanently)
- Preserves path and query parameters
- Sets proper Location header
- Logs redirect requests
The TLS manager generates Bun-compatible TLS options:
interface BunTLSOptions {
cert?: string | Buffer
key?: string | Buffer
ca?: string | Buffer
passphrase?: string
dhParamsFile?: string
}Default cipher suites prioritize security and performance:
TLS 1.3 (Preferred):
TLS_AES_256_GCM_SHA384- AES-256 with GCMTLS_CHACHA20_POLY1305_SHA256- ChaCha20-Poly1305TLS_AES_128_GCM_SHA256- AES-128 with GCM
TLS 1.2 (Fallback):
ECDHE-RSA-AES256-GCM-SHA384- Forward secrecy with AES-256ECDHE-RSA-AES128-GCM-SHA256- Forward secrecy with AES-128ECDHE-RSA-CHACHA20-POLY1305- Forward secrecy with ChaCha20
Set minimum TLS version to enforce security standards:
tls: {
enabled: true,
minVersion: 'TLSv1.3', // or 'TLSv1.2'
// ...
}Recommendation: Use TLS 1.3 for new deployments. TLS 1.2 is provided for backward compatibility.
Here's a complete example with all security features:
import { BunGateway } from 'bungate'
const gateway = new BunGateway({
server: {
port: 443,
development: false,
},
security: {
// TLS/HTTPS
tls: {
enabled: true,
cert: process.env.TLS_CERT_PATH,
key: process.env.TLS_KEY_PATH,
ca: process.env.TLS_CA_PATH,
minVersion: 'TLSv1.3',
cipherSuites: ['TLS_AES_256_GCM_SHA384', 'TLS_CHACHA20_POLY1305_SHA256'],
redirectHTTP: true,
redirectPort: 80,
},
// Input validation
inputValidation: {
maxPathLength: 2048,
maxHeaderSize: 16384,
maxHeaderCount: 100,
sanitizeHeaders: true,
},
// Security headers
securityHeaders: {
enabled: true,
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
contentSecurityPolicy: {
directives: {
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'"],
'img-src': ["'self'", 'https:'],
'frame-ancestors': ["'none'"],
},
},
xFrameOptions: 'DENY',
xContentTypeOptions: true,
},
// Size limits
sizeLimits: {
maxBodySize: 10 * 1024 * 1024,
maxHeaderSize: 16 * 1024,
maxHeaderCount: 100,
},
// JWT key rotation
jwtKeyRotation: {
secrets: [
{
key: process.env.JWT_SECRET_PRIMARY,
algorithm: 'HS256',
kid: 'primary-2024',
primary: true,
},
{
key: process.env.JWT_SECRET_OLD,
algorithm: 'HS256',
kid: 'old-2023',
deprecated: true,
},
],
gracePeriod: 7 * 24 * 60 * 60 * 1000,
},
// Trusted proxies
trustedProxies: {
enabled: true,
trustedNetworks: ['cloudflare'],
maxForwardedDepth: 2,
},
},
routes: [
{
pattern: '/api/*',
target: 'http://backend:3000',
auth: {
secret: process.env.JWT_SECRET_PRIMARY,
jwtOptions: {
algorithms: ['HS256'],
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
},
},
rateLimit: {
max: 1000,
windowMs: 60000,
},
},
],
})
await gateway.listen()Recommended environment variables for production:
# TLS Configuration
TLS_CERT_PATH=/path/to/cert.pem
TLS_KEY_PATH=/path/to/key.pem
TLS_CA_PATH=/path/to/ca.pem
# JWT Secrets
JWT_SECRET_PRIMARY=your-primary-secret-key
JWT_SECRET_OLD=your-old-secret-key
# Optional: JWKS
JWKS_URI=https://auth.example.com/.well-known/jwks.jsonThe TLS manager logs important events:
// Certificate loading
logger.info('Loading TLS certificates')
logger.info('TLS certificates loaded successfully')
// Certificate validation
logger.warn('Certificate expires in 30 days', { expiresAt: '2024-12-31' })
// HTTP redirect
logger.info('HTTP redirect server started', { port: 80, httpsPort: 443 })
// JWT key rotation
logger.warn('JWT verified with deprecated key', { kid: 'old-2023' })See the examples/security-hardened.ts file for a complete working example with all security features enabled.