Initial commit

This commit is contained in:
2026-04-17 23:26:01 +00:00
commit 2ea4ca5d52
409 changed files with 63459 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
---
title: Configure Reverse Proxy Correctly
impact: LOW-MEDIUM
impactDescription: HTTPS, caching, rate limiting, and security headers
tags: production, nginx, caddy, https, proxy
---
## Configure Reverse Proxy Correctly
Use a reverse proxy (Nginx, Caddy) for HTTPS termination, caching, rate limiting, and security headers.
**Incorrect (exposing PocketBase directly):**
```bash
# Direct exposure - no HTTPS, no rate limiting
./pocketbase serve --http="0.0.0.0:8090"
# Port forwarding without proxy
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8090
# Still no HTTPS!
```
**Correct (Caddy - simplest option):**
```caddyfile
# /etc/caddy/Caddyfile
myapp.com {
# Automatic HTTPS via Let's Encrypt
reverse_proxy 127.0.0.1:8090 {
# Required for SSE/Realtime
flush_interval -1
}
# Security headers
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
# Restrict admin UI to internal/VPN networks
# @admin path /_/*
# handle @admin {
# @blocked not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
# respond @blocked 403
# reverse_proxy 127.0.0.1:8090
# }
# Rate limiting (requires caddy-ratelimit plugin)
# Install: xcaddy build --with github.com/mholt/caddy-ratelimit
# Without this plugin, use PocketBase's built-in rate limiter (--rateLimiter=true)
# rate_limit {
# zone api {
# key {remote_host}
# events 100
# window 1m
# }
# }
}
```
**Correct (Nginx configuration):**
```nginx
# /etc/nginx/sites-available/pocketbase
# Rate limit zones must be defined in http context (e.g., /etc/nginx/nginx.conf)
# limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
upstream pocketbase {
server 127.0.0.1:8090;
keepalive 64;
}
server {
listen 80;
server_name myapp.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name myapp.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Note: X-XSS-Protection is deprecated and can introduce vulnerabilities.
# Use Content-Security-Policy instead.
location / {
proxy_pass http://pocketbase;
proxy_http_version 1.1;
# Headers
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;
# SSE/Realtime support
proxy_set_header Connection '';
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
# Timeouts
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# Rate limit API endpoints
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://pocketbase;
proxy_http_version 1.1;
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_set_header Connection '';
proxy_buffering off;
}
# Static file caching
location /api/files/ {
proxy_pass http://pocketbase;
proxy_cache_valid 200 1d;
expires 1d;
add_header Cache-Control "public, immutable";
}
# Gzip compression
gzip on;
gzip_types text/plain application/json application/javascript text/css;
gzip_min_length 1000;
}
```
**Docker Compose with Caddy:**
```yaml
# docker-compose.yml
version: '3.8'
services:
pocketbase:
# NOTE: This is a third-party community image, not officially maintained by PocketBase.
# For production, consider building your own image from the official PocketBase binary.
# See: https://pocketbase.io/docs/going-to-production/
image: ghcr.io/muchobien/pocketbase:latest
restart: unless-stopped
volumes:
- ./pb_data:/pb_data
environment:
- PB_ENCRYPTION_KEY=${PB_ENCRYPTION_KEY}
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- pocketbase
volumes:
caddy_data:
caddy_config:
```
**Key configuration points:**
| Feature | Why It Matters |
|---------|---------------|
| HTTPS | Encrypts traffic, required for auth |
| SSE support | `proxy_buffering off` for realtime |
| Rate limiting | Prevents abuse |
| Security headers | XSS/clickjacking protection |
| Keepalive | Connection reuse, better performance |
Reference: [PocketBase Going to Production](https://pocketbase.io/docs/going-to-production/)