Nginx Reverse Proxy
Running CloakProbe behind Nginx provides SSL termination, load balancing, and additional security.
Architecture
Section titled “Architecture”CloakProbe supports two deployment architectures:
1. Cloudflare Mode: Internet → Cloudflare CDN → Nginx → CloakProbe (mode=cloudflare)
2. Nginx Mode (Direct): Internet → Nginx → CloakProbe (mode=nginx)Security Warning
Section titled “Security Warning”Configuration 1: Behind Cloudflare
Section titled “Configuration 1: Behind Cloudflare”For domains proxied through Cloudflare (orange cloud enabled).
CloakProbe Config
Section titled “CloakProbe Config”[server]bind_address = "127.0.0.1"port = 8080mode = "cloudflare"Nginx Config
Section titled “Nginx Config”server { listen 80; server_name ip.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; server_name ip.example.com;
# SSL certificates ssl_certificate /etc/letsencrypt/live/ip.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ip.example.com/privkey.pem;
# SECURITY: Only allow Cloudflare IPs include /etc/nginx/snippets/cloudflare-only.conf;
# Disable logging for privacy access_log off; error_log /dev/null crit;
location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme;
# Client IP (converted by real_ip_header in snippet) proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; }}Cloudflare IP Restriction
Section titled “Cloudflare IP Restriction”Create a reusable snippet that restricts connections to Cloudflare IPs:
# Cloudflare IPv4 rangesset_real_ip_from 173.245.48.0/20;set_real_ip_from 103.21.244.0/22;set_real_ip_from 103.22.200.0/22;set_real_ip_from 103.31.4.0/22;set_real_ip_from 141.101.64.0/18;set_real_ip_from 108.162.192.0/18;set_real_ip_from 190.93.240.0/20;set_real_ip_from 188.114.96.0/20;set_real_ip_from 197.234.240.0/22;set_real_ip_from 198.41.128.0/17;set_real_ip_from 162.158.0.0/15;set_real_ip_from 104.16.0.0/13;set_real_ip_from 104.24.0.0/14;set_real_ip_from 172.64.0.0/13;set_real_ip_from 131.0.72.0/22;
# Cloudflare IPv6 rangesset_real_ip_from 2400:cb00::/32;set_real_ip_from 2606:4700::/32;set_real_ip_from 2803:f800::/32;set_real_ip_from 2405:b500::/32;set_real_ip_from 2405:8100::/32;set_real_ip_from 2a06:98c0::/29;set_real_ip_from 2c0f:f248::/32;
# Trust CF-Connecting-IP as the real client IPreal_ip_header CF-Connecting-IP;
# DENY all non-Cloudflare IPsallow 173.245.48.0/20;allow 103.21.244.0/22;# ... (add all ranges)deny all;Configuration 2: Direct Nginx (No Cloudflare)
Section titled “Configuration 2: Direct Nginx (No Cloudflare)”For domains that bypass Cloudflare entirely.
CloakProbe Config
Section titled “CloakProbe Config”[server]bind_address = "127.0.0.1"port = 8080mode = "nginx"Nginx Config
Section titled “Nginx Config”server { listen 80; listen [::]:80; server_name ip-direct.example.com; return 301 https://$host$request_uri;}
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ip-direct.example.com;
ssl_certificate /etc/letsencrypt/live/ip-direct.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ip-direct.example.com/privkey.pem;
access_log off; error_log /dev/null crit;
location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme;
# SECURITY: Always use $remote_addr proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;
# SECURITY: Clear spoofed Cloudflare headers proxy_set_header CF-Connecting-IP ""; proxy_set_header CF-IPCountry ""; proxy_set_header CF-Ray ""; proxy_set_header CF-Visitor ""; }}Configuration 3: IPv6-Only Instance
Section titled “Configuration 3: IPv6-Only Instance”For dual-stack network detection. This instance only listens on IPv6.
CloakProbe Config
Section titled “CloakProbe Config”[server]bind_address = "::1"port = 8082mode = "nginx"region = "eu-central-v6"Nginx Config
Section titled “Nginx Config”# NO IPv4 listener - IPv6 only!server { listen [::]:80 ipv6only=on; server_name ip6.example.com; return 301 https://$host$request_uri;}
server { listen [::]:443 ssl http2 ipv6only=on; server_name ip6.example.com;
ssl_certificate /etc/letsencrypt/live/ip6.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ip6.example.com/privkey.pem;
access_log off; error_log /dev/null crit;
location / { proxy_pass http://[::1]:8082; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;
# Clear spoofed headers proxy_set_header CF-Connecting-IP ""; }}DNS Setup
Section titled “DNS Setup”; Only AAAA record, no A record!ip6.example.com. IN AAAA 2001:db8::1Dual-Stack Detection Setup
Section titled “Dual-Stack Detection Setup”Use multiple CloakProbe instances to detect client network capabilities:
| Endpoint | Protocol | Purpose |
|---|---|---|
ip.example.com | IPv4 + IPv6 (via CF) | Primary, full features |
ip4.example.com | IPv4 only | Test IPv4 connectivity |
ip6.example.com | IPv6 only | Test IPv6 connectivity |
Client-Side Detection
Section titled “Client-Side Detection”async function detectNetworkStack() { const results = { ipv4: null, ipv6: null };
try { const v4 = await fetch('https://ip4.example.com/api/v1/json'); results.ipv4 = (await v4.json()).ip; } catch (e) {}
try { const v6 = await fetch('https://ip6.example.com/api/v1/json'); results.ipv6 = (await v6.json()).ip; } catch (e) {}
return { ...results, dualStack: results.ipv4 !== null && results.ipv6 !== null };}SSL Certificates
Section titled “SSL Certificates”Let’s Encrypt (Recommended)
Section titled “Let’s Encrypt (Recommended)”sudo apt install certbot python3-certbot-nginxsudo certbot --nginx -d ip.example.comCloudflare Origin CA
Section titled “Cloudflare Origin CA”For Cloudflare Full (strict) mode, use Cloudflare Origin CA certificates.
Performance Tuning
Section titled “Performance Tuning”Worker Connections
Section titled “Worker Connections”In /etc/nginx/nginx.conf:
events { worker_connections 4096; use epoll; multi_accept on;}Upstream with Keepalive
Section titled “Upstream with Keepalive”upstream cloakprobe { server 127.0.0.1:8080; keepalive 32;}Troubleshooting
Section titled “Troubleshooting”502 Bad Gateway
Section titled “502 Bad Gateway”# Check if CloakProbe is runningsudo systemctl status cloakprobe
# Check if port is listeningss -tlnp | grep 8080Permission denied
Section titled “Permission denied”# Test connection as nginx usersudo -u www-data curl http://127.0.0.1:8080/healthzIP showing as Cloudflare IP
Section titled “IP showing as Cloudflare IP”Ensure real_ip_header CF-Connecting-IP is configured and the snippet is included.