# Caddy in front of the server (/etc/caddy/Caddyfile): a Let's Encrypt
# certificate for your name, HTTPS on 443, http -> https, WebSockets passed on.
# The server runs with --behind-proxy --http-port 8080 --host game.example.com
# (deploy/nfsu2-web.service, A).  Replace game.example.com with your name.
#
# Caddy names no software: no Server or Via header on answers, on its
# redirects or on its errors (a 502 while the server restarts), and a request
# for any other name (a scanner on the bare address) gets no answer at all.
#
# Behind Cloudflare's proxy (orange cloud): uncomment this first block too (it
# must stay the first thing in the file), and use the Cloudflare site block at
# the end instead of the game.example.com block.  Caddy then believes
# CF-Connecting-IP only from Cloudflare's addresses
# (web/server-rs/crates/headers/cloudflare-ips.txt, as of 2026-10-18;
# https://www.cloudflare.com/ips/), and only from a connection that showed
# Cloudflare's origin-pull certificate: any Cloudflare customer connects from
# those addresses too (a Worker's TCP socket) and could claim any client
# address (the second security audit, 2026-10-22).  Turn Authenticated Origin
# Pulls on in Cloudflare's dashboard (SSL/TLS, Origin Server) first, and copy
# deploy/cloudflare-origin-pull-ca.pem to /etc/caddy/.  Even so, let only
# Cloudflare reach 443 and 80 (cloudflare-only.sh).
#{
#	servers {
#		trusted_proxies static 173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22 2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32
#		client_ip_headers CF-Connecting-IP
#	}
#}

# http -> https for your name (Caddy's own redirect would say "Server: Caddy")
http://game.example.com {
	header -Server
	redir https://{host}{uri} 308
}

# any other name (Host: the bare address, or a name a scanner made up): the
# connection is closed without an answer.  On 80, Caddy's own catch-all
# redirect would name it; on 443, a request for another Host over a connection
# made for your name would get Caddy's empty answer.  (A TLS handshake for
# another name, or for none, fails already: Caddy has no certificate for it.)
:80 {
	abort
}
:443 {
	abort
}

game.example.com {
	reverse_proxy 127.0.0.1:8080 {
		# the address that connected, whatever CF-Connecting-IP a client sent (the server reads that header
		# with --real-ip-header cf-connecting-ip)
		header_up CF-Connecting-IP {client_ip}
		# one connection to the server per request in progress, at most this many: fewer than the 16000 the
		# server takes with the unit's --max-peers 2000 (8 per player; 1024 with --max-peers 128 or fewer, then
		# lower this too).  The server itself holds each client address to 64 requests at once.
		transport http {
			max_conns_per_host 15900
		}
	}
	# say nothing about the software: not on answers, nor on errors
	header -Server
	header -Via
	handle_errors {
		header -Server
	}
}

# Behind Cloudflare's proxy, with a Cloudflare Origin CA certificate instead of
# Let's Encrypt (SSL/TLS mode Full (strict)), and Cloudflare's origin-pull
# certificate asked of every connection; the server also gets
# --real-ip-header cf-connecting-ip:
#game.example.com {
#	tls /etc/caddy/origin.pem /etc/caddy/origin.key {
#		client_auth {
#			mode require_and_verify
#			trust_pool file /etc/caddy/cloudflare-origin-pull-ca.pem
#		}
#	}
#	reverse_proxy 127.0.0.1:8080 {
#		header_up CF-Connecting-IP {client_ip}
#		transport http {
#			max_conns_per_host 15900
#		}
#	}
#	header -Server
#	header -Via
#	handle_errors {
#		header -Server
#	}
#}
