Edge security & TLS
How the edge blocks direct-IP scans, hides your domain from scanners, locks the origin to Cloudflare, and which TLS mode to choose.
Once your cluster has a public hostname, the OpenResty edge stops behaving like an open IP and becomes a locked-down front door. It answers only the hostnames you configured, hides your real domain from internet scanners, and can refuse any connection that did not arrive through Cloudflare. This page explains each protection, the available TLS modes, and how to add a host firewall for defense in depth.
Eval and IP-only installs stay open
These protections activate only after a hostname is configured. An IP-only evaluation
install (no hostname, no TLS) intentionally stays reachable over http://<ip> so you
can try the cluster before wiring up DNS. Add a hostname before you rely on any of the
hardening below.
Host and direct-IP blocking
With a hostname set, the edge only serves requests whose Host header (or TLS SNI)
matches a configured hostname. Anything else, including a raw request to the server's IP
address or an unknown Host, is dropped with HTTP 444. That is nginx's "close the
connection without a response" status, so scanners and bots get nothing back to
fingerprint.
| Request | Result |
|---|---|
https://your-domain/ | Served normally |
https://<origin-ip>/ (direct IP) | Connection dropped, 444 |
Unknown or spoofed Host header | Connection dropped, 444 |
| ACME challenge on port 80 | Allowed, so certificate issuance still works |
This closes the common mistake where hitting the origin IP falls through to the first
server block and quietly serves your app. A strict catch-all default_server now owns
port 80 and port 443, so unmatched traffic never reaches the application.
Anti-scanning: the decoy certificate
Services like Shodan and Censys crawl the whole IPv4 space, connect to port 443 on every IP, and record the certificate. A real certificate leaks your domain in its Subject Alternative Name (SAN), which lets anyone map an IP straight back to your site.
To prevent that, the catch-all default_server presents a decoy self-signed
certificate with a bogus common name and no real SAN. A scanner hitting your IP reads
only the decoy, so your true domain never appears in scan results. The real certificate
is served exclusively to connections whose SNI matches a configured hostname.
The edge also sets server_tokens off, so responses do not advertise the server version.
Together the decoy cert and hidden version give IP scanners nothing useful.
Why self-signed is fine here
The decoy is never validated by a real client, because real clients arrive with the correct SNI and get the real certificate. Its only job is to answer scanners with a cert that reveals nothing.
Cloudflare origin lock
When Cloudflare fronts your cluster, all legitimate traffic arrives from Cloudflare's IP ranges. Anyone who discovers your origin IP could otherwise bypass Cloudflare (and its WAF, rate limits, and TLS) by connecting to the origin directly.
The origin lock closes that gap. The edge reads the real connecting IP and refuses any connection that is not from a current Cloudflare range or from loopback, returning 444. Cloudflare's published IPv4 and IPv6 ranges are fetched at render time and cached, with a bundled fallback list in the image so a render never depends on network access.
The lock is on by default for Cloudflare mode and applies to both Flexible (port 80) and Full (port 443). Loopback is always allowed so on-box health checks keep working.
TLS modes
Traffic to the end user is always HTTPS. You pick how TLS is terminated and how the edge presents itself to the origin. Choose the mode that matches how requests reach your host.
Use this when Cloudflare proxies your domain (the orange cloud). Cloudflare terminates TLS with the browser, then connects to your origin. Pick a sub-mode that matches your Cloudflare SSL/TLS setting.
- Flexible: the origin serves plain HTTP on port 80 and Cloudflare handles TLS at the edge. No origin certificate is needed. Simplest to set up, but the Cloudflare-to-origin hop is unencrypted, so rely on the origin lock plus a host firewall.
- Full: the origin serves HTTPS on port 443 and Cloudflare connects over TLS. Choose
the origin certificate source:
- Self-signed (default): a generic self-signed origin certificate the edge generates automatically. Valid for Cloudflare Full because Full does not validate the origin certificate.
- Cloudflare Origin CA: for Full (strict). The edge issues a long-lived origin certificate through the Cloudflare API using a one-time API token. The token is used once and never stored.
Optionally enable mTLS / Authenticated Origin Pulls (Cloudflare with Full only). The edge then requires a client certificate from Cloudflare's origin-pull CA, so only Cloudflare can complete the TLS handshake. This uses the bundled global Cloudflare CA by default, or a zone-level CA you upload. See Authenticated Origin Pulls.
Behind Cloudflare, use Full (not Let's Encrypt)
Standalone Let's Encrypt uses an HTTP-01 challenge on port 80, which Cloudflare's proxy intercepts, so it cannot complete when DNS points at Cloudflare. Let's Encrypt behind Cloudflare (DNS-01) is not supported. Behind Cloudflare, use Full with a self-signed or Origin CA certificate instead.
Paste a PEM certificate and private key from your own CA or provider. The origin
serves HTTPS on port 443 and redirects port 80 to 443. The files are stored on the
cm-state volume and served directly. Use this when you already have a certificate you
want to run, with or without an upstream proxy.
The edge issues and renews a certificate on-box with lua-resty-acme, using the
HTTP-01 challenge. The origin serves HTTPS on port 443 and answers the challenge on
port 80. This works only when DNS points straight at the origin, so nothing
intercepts the port 80 challenge.
Prerequisites:
- An A record for your hostname resolves to this host.
- Ports 80 and 443 are open. Port 80 answers the challenge, port 443 serves the cert.
Do not use this mode behind a proxy that intercepts port 80, such as Cloudflare's orange cloud. In that case use Cloudflare + Full instead.
Reference
For how Cloudflare's Flexible, Full, and Full (strict) settings behave end to end, see the Cloudflare SSL/TLS encryption modes documentation. Match the origin sub-mode you pick here to your Cloudflare dashboard setting.
Download the active origin certificate
You can view and download the certificate the origin is currently serving from Settings in the console. The download is PEM only and never includes the private key. It shows the certificate along with its fingerprint and expiry, which is handy for uploading the origin certificate into Cloudflare (Full strict) or verifying what the edge presents.
The download is hidden for Cloudflare + Flexible, because in that mode the origin has no certificate (TLS lives entirely at the Cloudflare edge).
Defense in depth: host firewall
The origin lock lives inside the edge. Add a host firewall so unwanted traffic never even reaches OpenResty. When you front the cluster with Cloudflare, restrict ports 80 and 443 to Cloudflare's ranges at the operating-system level.
Allow only Cloudflare ranges to reach 80 and 443, then deny the rest. Repeat the allow
lines for every range in Cloudflare's IP list:
# For each Cloudflare IPv4 and IPv6 range:
sudo ufw allow from 173.245.48.0/20 to any port 80 proto tcp
sudo ufw allow from 173.245.48.0/20 to any port 443 proto tcp
# ...repeat for every range...
# Then deny direct access from everywhere else:
sudo ufw deny 80/tcp
sudo ufw deny 443/tcp
sudo ufw enableBuild a named set of Cloudflare ranges and accept only those on 80 and 443:
table inet filter {
set cloudflare_v4 {
type ipv4_addr
flags interval
elements = { 173.245.48.0/20, 103.21.244.0/22 } # add every Cloudflare range
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
tcp dport { 80, 443 } ip saddr @cloudflare_v4 accept
# add an ipv6 set + rule for Cloudflare's IPv6 ranges
}
}Keep the firewall list current
Cloudflare's ranges change occasionally. Refresh your firewall rules from the Cloudflare IP list periodically, or automate it, so a new range is never blocked by mistake. The edge origin lock refreshes its own copy at render time.