Lucky STUN + Cloudflare 302: Keep a Stable Hostname for a Dynamic Public Port
Use Lucky STUN to obtain a public mapping, then update a Cloudflare Single Redirect through a webhook so a stable hostname temporarily redirects to the current IP and port. Includes 303 limitations, TLS, app compatibility, and Tunnel alternatives.
This guide is not about ordinary DDNS, and it does not place Cloudflare's reverse proxy in front of a random STUN port. It focuses on one combined flow: Lucky obtains a public IP:dynamic port through STUN, then a webhook updates the target of a Cloudflare Single Redirect whenever that mapping changes.
Visitors only need to remember go.example.com. Their browser first reaches Cloudflare, receives a temporary redirect, and then connects directly to the current STUN endpoint. This is the same basic pattern as the referenced 301 guide, but this article uses 302. A NAT mapping is temporary and should not be taught to browsers or search engines as a permanent destination.
This guide uses the real Lucky 2.27.2 and Cloudflare Dashboard interfaces and cross-checks their official documentation. Written examples use documentation-only values such as example.com and 192.0.2.0/24. The captured images do not expose real hostnames, IP addresses, ports, instance names, traffic counters, or tokens; areas that originally contained dashboard data have been irreversibly blurred.
The short recommendation: use 302 for a temporary browser entry point, not 301. Cloudflare Single Redirect currently supports 301, 302, 307, and 308, but not 303. Apps, APIs, remote desktop clients, and non-HTTP protocols usually need 307, Cloudflare Tunnel, a VPN, or a native transport instead.
Understand the exact request path
First request
Browser opens https://go.example.com/photos?id=42
└─ Cloudflare Single Redirect returns 302
Location: http://203.0.113.10:45678/photos?id=42
└─ Browser connects directly to the STUN mapping
└─ Lucky relay (or router port-forward)
└─ Private web service
When the mapping changes
Lucky obtains 203.0.113.10:51234
└─ STUN webhook PATCHes the Cloudflare Rulesets API
└─ The next visit receives the new Location
“Hostname-only access” means only that the address initially typed by the visitor has no port. After the redirect, the address bar still shows the real IP or target hostname and its port. Cloudflare handles only the first request. The application traffic does not pass through Cloudflare, so WAF, cache, Access, and origin hiding do not protect the second connection.
| Approach | Visitor entry point | Second connection | Good fit |
|---|---|---|---|
| Direct STUN | IP/hostname plus dynamic port | Visitor connects directly to home network | TCP/UDP clients that already know the port |
| STUN + Cloudflare 302 | Stable HTTPS hostname, then dynamic address appears | Visitor connects directly to home network | Browser GET pages that may expose the endpoint |
| Cloudflare Tunnel | Stable HTTPS hostname | Through Cloudflare Tunnel | Web, APIs, admin panels, Access/WAF requirements |
Path one: configure Lucky STUN
1. Verify the network conditions first
The official Lucky STUN guide describes NAT1 as the target environment. It also warns that a public mapping is not guaranteed to remain fixed and that its change interval is unpredictable. NAT labels vary across products, so do not rely solely on the string shown by a router. Treat Lucky's rule log and a real off-network connection test as the source of truth.
Before creating a rule:
- If the optical modem performs the dial-up connection, Lucky recommends pointing its DMZ at the main router. Skip this when the router performs dial-up.
- Running Lucky on the main router is the simplest topology. If Lucky runs on a LAN host, the main router also needs UPnP, a port-forward, or a carefully scoped DMZ configuration.
- Confirm that the operating-system kernel and firewall allow the required TCP or UDP behavior. A proxy, double NAT, or multi-WAN policy can change the result.
- Do not expose the Lucky administration panel as your first test. Use a disposable service without sensitive data.
2. Enable the STUN module
Open:
Lucky → Intranet Traversal → STUN → Settings
Enable the module and review the global STUN server list. The interface links to a public tested STUN host list. A longer list is not automatically a better list; keep endpoints that are reachable and tested from your network, and do not paste an untrusted bulk list into production.
Real interface capture. It contains only public STUN server addresses and no private network value or credential.
A webhook can notify another system when a public mapping changes. Enable it only when you have a concrete automation need, and make sure secrets in its URL, headers, or payload cannot enter screenshots or logs.
3. Create the first rule in simple mode
Open the traversal rule list and choose “Add rule.” Use simple mode for the first test.
The form is blank. Real rule names, IP addresses, ports, and traffic counters in the background were irreversibly blurred.
| Field | Recommendation |
|---|---|
| Rule name | Use an internal label that does not reveal a device model, physical location, or business function |
| Traversal type | Choose IPv4-TCP or IPv4-UDP according to the service; one rule handles one protocol |
| Local channel port | When set manually, use an unused, globally unique port. Lucky recommends a value above 10240. With UPnP, its guide permits 0 so Lucky can choose a port |
| Automatic firewall opening | Enable only when Lucky runs on a supported router and you understand the resulting firewall rule |
| UPnP / NAT-PMP | Use only when Lucky is a LAN host and the router has the corresponding feature enabled; otherwise leave them off |
| Do not use Lucky's built-in forwarding | Leave off for the first test. Consider router forwarding for performance only after traversal is known to work |
| Disable channel validation | Leave off; this is a debugging option |
| Target address | Enter a private IP or hostname without http:// or https:// |
| Target port | Enter the port on which the private service listens; required for Lucky's built-in forwarding |
A fully fictional TCP example:
Traversal type: IPv4-TCP
Local channel port: 12080
Target address: 192.0.2.10
Target port: 8080
192.0.2.10:8080 uses an RFC 5737 documentation address and is only a placeholder. Redact a real private address and port together before publishing a screenshot or log; that combination is often enough to identify a service.
4. Do not “optimize” custom mode without evidence
In Lucky 2.27.2, custom mode includes interface or IP binding, IP allow/deny rules, TCP connection limits, UDP session limits, encryption, STUN wait time, heartbeat interval, retries, and log controls. These options interact with one another. A default visible on one installation is not a universal recommendation.
Change an advanced value only for a reproducible reason:
- bind to an IP or interface only for multi-WAN or policy-routing cases;
- cap connections and UDP sessions according to the service and device resources;
- change one wait, heartbeat, or retry value at a time and retain before/after logs;
- automatic retry can improve availability, but can also turn a bad configuration into continuous traffic;
- TLS or stream encryption does not replace application authentication and authorization.
5. Test from a real external network
After saving, inspect the rule-specific log. Then disable Wi-Fi and test from a mobile network or a second broadband connection. Verify at least:
- Lucky obtained a public mapping;
- a remote connection appears in the rule log;
- the firewall or router sent traffic to the correct Lucky port;
- the service listens on the expected address and network namespace;
- clients or automation can learn the new address when the public port changes.
If the log contains no incoming attempt at all, investigate the ISP NAT, modem/router mapping, and firewall before repeatedly changing the target service.
Core setup: use a Cloudflare 302 to follow the STUN port
STUN returns a public IP and a public port. A/AAAA records store only the IP; they cannot dynamically attach a port to an ordinary browser URL. The useful part of the referenced design is that DNS only brings a stable entry hostname to Cloudflare. The current IP:port is stored in the redirect response's Location and kept up to date by a Lucky webhook.
1. Why 302, not 301 or 303
| Status | Meaning and method behavior | Fit for this guide |
|---|---|---|
| 301 | Permanent move; clients and search engines may retain the target | Wrong for a changing NAT mapping |
| 302 | Temporary move; broadly suitable for browser GET pages | Recommended entry point |
| 303 | Requires the follow-up request to use GET; often used after POST | Not accepted by Single Redirect |
| 307 | Temporary move while preserving method and body | Consider for compatible apps/APIs |
| 308 | Permanent move while preserving the method | Wrong for a changing mapping |
Cloudflare's current Single Redirect settings list 301, 302, 307, and 308. Cloudflare's general 3xx documentation explains the HTTP semantics of 303, but that does not make 303 valid for the Rulesets API http_request_dynamic_redirect action. Changing the JSON below to status_code: 303 is therefore likely to fail API validation.
If a workflow must return 303, generate it in a Cloudflare Worker or at your own origin. That is a different architecture from Single Redirect. Use 302 for ordinary browser GET pages. If an API must preserve POST or PUT bodies, reassess the architecture before mechanically switching to 307.
2. Give the entry point and direct target separate hostnames
| Hostname | Cloudflare mode | Purpose |
|---|---|---|
go.example.com | Proxied (orange cloud) | Stable visitor entry point that triggers the redirect |
origin-direct.example.com | DNS only (optional) | Direct target updated by Lucky DDNS when valid hostname-based TLS is required |
Cloudflare requires incoming traffic for a Single Redirect hostname to be proxied. go.example.com must therefore be orange-clouded. A matching rule returns 302 before contacting the record's origin. Unmatched traffic may continue to origin, so cover the complete hostname and make unmatched requests fail safely instead of pointing the record at a private admin panel.
Two target styles are possible:
Simplest: http://public-IP:dynamic-port/path
Better for HTTPS: https://origin-direct.example.com:dynamic-port/path
Redirecting to an HTTPS IP commonly fails certificate hostname validation. For HTTPS, let Lucky DDNS update a DNS-only target hostname and issue an origin certificate for origin-direct.example.com. Either style exposes the target IP and port on the second connection and bypasses Cloudflare WAF and Access.
3. Create a 302 Dynamic Redirect in Cloudflare
Open:
Cloudflare Dashboard → target Zone → Rules → Redirect Rules
→ Create rule → Single Redirects
Create a placeholder rule:
Rule name: Lucky STUN temporary redirect
Match expression: (http.host eq "go.example.com")
Type: Dynamic
Target expression: concat("http://192.0.2.10:12080", http.request.uri.path)
Status code: 302
Preserve query string: enabled
192.0.2.10 is documentation space. The placeholder only lets Cloudflare create the ruleset and rule identifiers; it is not a real connectivity target. After deployment, use curl -I to confirm a 302 response and inspect whether Location retains the path and query string.
4. Create a least-privilege API token
Create a dedicated token for Lucky:
Permissions: Zone → Single Redirect → Edit
Resources: Include → Specific zone → example.com
Do not use the Global API Key or add DNS, Workers, or account administration permissions. This token can change where the public entry hostname sends visitors, so a leak could be abused for phishing.
The webhook also needs three non-secret identifiers: ZONE_ID, the dynamic redirect RULESET_ID, and the target RULE_ID. You can inspect the request while editing a rule in the Dashboard, or list zone rulesets through the API, find the entry with phase http_request_dynamic_redirect, and then read its rules. Redact these identifiers in public screenshots anyway.
5. Configure the Lucky rule webhook
Edit the relevant STUN rule and enable its webhook. Use the public API endpoint, not the Dashboard's internal /api/v4/ URL:
URL:
https://api.cloudflare.com/client/v4/zones/<ZONE_ID>/rulesets/<RULESET_ID>/rules/<RULE_ID>
Method: PATCH
Headers:
Authorization: Bearer <CLOUDFLARE_API_TOKEN>
Content-Type: application/json
Use a 302 request body:
{
"action": "redirect",
"description": "Lucky STUN temporary redirect",
"enabled": true,
"expression": "(http.host eq \"go.example.com\")",
"action_parameters": {
"from_value": {
"status_code": 302,
"target_url": {
"expression": "concat(\"http://#{ipAddr}\", http.request.uri.path)"
},
"preserve_query_string": true
}
}
}
Lucky replaces #{ipAddr} with the current public address and port when the STUN webhook runs. Cloudflare consequently receives an expression such as:
concat("http://203.0.113.10:45678", http.request.uri.path)
For a DNS-only target hostname with HTTPS, update that hostname through Lucky DDNS and use:
"expression": "concat(\"https://origin-direct.example.com:#{port}\", http.request.uri.path)"
#{ipAddr} and #{port} are Lucky webhook variables, not Cloudflare Rules language fields. Use straight ASCII quotation marks and preserve the JSON escapes around quotes inside the expression.
Set Lucky's success check to look for:
"success": true
Serialization may include spaces, so do not treat one manual trigger as proof of long-term operation. Inspect both the Lucky webhook log and Cloudflare's API response, then read the rule again to confirm its target actually changed.
6. Verify the full path from another network
Use mobile data or another connection and inspect the first hop:
curl -sS -D - -o /dev/null "https://go.example.com/photos?id=42"
Expected fields:
HTTP/2 302
location: http://203.0.113.10:45678/photos?id=42
Then verify:
- the response is 302 rather than an older 301 rule;
- the IP, port, path, and query string in
Locationare correct; - a new STUN mapping causes the webhook to publish the new port;
- an incognito window and a second device are not using a cached permanent redirect;
- the direct service has its own authentication, rate limits, brute-force protection, and TLS.
curl -I checks HEAD only, and some services treat HEAD differently from GET. The -D - -o /dev/null form above performs a GET without printing a private response body.
7. What this 302 design does not solve
- It does not truly hide the port; the address bar and
Locationreveal it. - It does not hide the home public IP; visitors connect directly to it.
- It does not make apps, TV clients, remote desktop, or custom protocols understand HTTP redirects.
- It does not place the second request behind Cloudflare WAF, cache, or Access.
- It does not fix Cookie Domain, CORS, CSRF, absolute URLs, Host validation, or TLS certificates.
- It is an HTTP/HTTPS entry mechanism; UDP cannot establish a session through HTTP 302.
Cloudflare Redirect is therefore not an orange-cloud proxy for a STUN port. The orange cloud covers only the first HTTP request to the stable entry hostname.
Alternative: run Cloudflare Tunnel inside Lucky
For a website, API, or administration page, Cloudflare Tunnel is often simpler. cloudflared connects outward from the private network, so it needs neither a public IP nor an inbound router port. The architecture is documented in the Cloudflare Tunnel overview.
1. Create a remotely managed tunnel in Cloudflare
In the Cloudflare Dashboard, open:
Networking → Tunnels → Create a tunnel → Cloudflared
Choose a name that does not contain a home address, device serial number, or personal name. After creation, the dashboard shows an installation command containing a Tunnel Token. Lucky needs the token value, not a screenshot or permanent copy of the whole command.
Real interface capture. No name was entered, no token was generated, and the tunnel was not created. The account sidebar is collapsed.
The token allows a new cloudflared connector to join the tunnel. Treat it as a password:
- never put it in Markdown, shell history, Git, a Dockerfile, or public environment variable;
- never expose it to browser-side code;
- use a separate token for each tunnel;
- if leaked, refresh the token in the tunnel details and remove old connections. Cloudflare also recommends regular Tunnel Token rotation.
2. Add a Tunnel instance in Lucky
Open:
Lucky → Intranet Traversal → Cloudflared → Add instance
Select Tunnel mode and configure:
The Tunnel Token and API Token fields are blank. Existing instance information in the background was irreversibly blurred.
| Field | Recommendation |
|---|---|
| Instance note | Use a unique label that does not disclose a device or location |
| Token | Paste the Tunnel Token; redact the complete value before publication |
| Edge network type | Use automatic in general; pin IPv4 or IPv6 only for a diagnosed link problem |
| High-availability connections | Keep a reasonable count; more connections do not create unlimited availability |
| Connection protocol | Auto, HTTP/2, and QUIC depend on the network; test HTTP/2 when outbound UDP is restricted |
| Edge bind IP | Leave empty on a single-homed host; use it only to select an egress path on multi-WAN |
| Do not verify origin TLS | Turn this off in production. Prefer a correct origin server name or a trusted CA |
To let Lucky manage application routes and DNS automatically, add a separate Cloudflare API token. The current Lucky interface requests:
- Account → Cloudflare Tunnel → Edit;
- Zone → DNS → Edit;
- account and zone resources limited to the ones actually used.
If automatic management is unnecessary, leave that API token empty and manage the route and DNS in the Cloudflare Dashboard. Least privilege is safer than choosing “All” for convenience.
3. Add an application route
Open “Application routes” on the Tunnel instance and add a mapping such as:
Hostname: app.example.com
Path: /*
Target service: http://192.0.2.10:8080
The target service must include a protocol. Review the Cloudflare Tunnel routing guide for HTTP, HTTPS, TCP, and other supported service values. Use HTTP/HTTPS published application routes for normal browser access. Non-HTTP public-hostname traffic commonly requires the client to run cloudflared; arbitrary public TCP/UDP proxying may require Spectrum or a private-network route.
When one hostname has several path rules, put specific routes before a catch-all route. For example, /api/* should precede /*, or the broad rule may match first.
Use advanced settings only when necessary:
- Origin server name when the certificate hostname differs from the target address;
- Host header when the origin uses virtual-host routing;
- HTTP/2 to origin only when the origin supports it;
- connection, TLS, and keep-alive timeouts based on logs and service behavior;
- disable origin TLS verification only as a temporary diagnostic. Cloudflare also describes
noTLSVerifyas a last resort that is not recommended for production.
4. Create the CNAME
Each Tunnel receives a target shaped like:
<TUNNEL_ID>.cfargotunnel.com
Create a CNAME for app.example.com pointing to it. Adding a published application route in the Cloudflare Dashboard normally creates the DNS record automatically. When Lucky manages the route, automatic DNS creation depends on the API token and current settings. Always confirm the result on the DNS page.
A Tunnel UUID is not an authentication secret equivalent to the token, but it is still an infrastructure identifier. There is no benefit to showing a real one in an article, so redact it as well.
5. Protect admin applications with Cloudflare Access
Tunnel determines how traffic reaches an origin; it does not automatically decide who may access the application. For admin panels, monitoring, and personal services, create an Access application:
Zero Trust → Access controls → Applications
→ Create new application → Self-hosted and private
Add the Tunnel hostname and create an Allow policy based on an email address, identity provider, device posture, or Service Token. Cloudflare documents the Action, Include, Require, and Exclude building blocks in its Access policy guide.
Do not rely on a port number in an Access-protected public URL. Cloudflare's port documentation notes that Access removes port numbers from URLs. Publish a standard HTTPS hostname and keep the private port inside the Tunnel target service.
Troubleshooting map
| Symptom | Check first |
|---|---|
| STUN does not obtain a mapping | NAT condition, dial-up location, double NAT, proxy software, kernel, and firewall |
| STUN has a mapping but no remote attempt appears | Public port, firewall, router forwarding, and ISP inbound restrictions |
| A remote attempt appears but the service fails | Target address, target port, protocol, and listen address |
| DNS resolves but the port is wrong | A/AAAA stores only the IP; check port-change notification and client configuration |
| The entry hostname still returns 301 | Check for an earlier Redirect/Page Rule and read the current rule's status_code |
| The webhook receives an API error | Verify Single Redirect: Edit, zone and rule IDs, JSON quoting, and expression escaping |
| The 302 Location still has an old port | Check whether Lucky triggered the webhook, whether the success check was a false positive, and whether another automation overwrote the rule |
| Browser works but an app cannot sign in | Confirm redirect support, Host handling, cookies, authentication callbacks, and TLS hostname assumptions |
| Redirecting to an HTTPS IP causes a certificate error | Use a DNS-only target hostname with a matching certificate or switch to Tunnel; do not disable certificate validation |
| Tunnel is connected but returns 502 | Reachability from Lucky to the target, protocol, port, and origin listen address |
| An HTTPS origin returns an x509 error | Set the correct origin server name or CA; do not permanently skip verification |
| Tunnel hostname returns 1016 | CNAME account, running connector, and Tunnel UUID |
| A path reaches the wrong service | Route order; put specific paths before /* |
| QUIC cannot connect | Check outbound UDP and compare with HTTP/2 |
Change one variable at a time and record the time of each change. Logs are valuable, but redact public and private IPs, ports, hostnames, request paths, cookies, Authorization headers, tokens, and instance labels before sharing them.
Privacy checklist before publishing a screenshot
Lucky's rule list is dense enough for one screenshot to reveal a home network topology. Check every image for:
- the Lucky admin address, port, and hidden entry path;
- public IPs, mapped ports, private IPs, and target ports;
- domains, hostnames, instance labels, rule names, and device names;
- Tunnel Tokens, Cloudflare API Tokens, and Access Service Tokens;
- Account IDs, Zone IDs, Tunnel UUIDs, and CNAME targets;
- request paths, webhook URLs, and request headers;
- logs, visitor IPs, traffic counters, and enabled states;
- browser tabs, bookmarks, profile avatars, and email addresses.
Partially blurring a token is not enough. A safer article uses a redrawn diagram or replaces every real value with placeholders such as app.example.com, 192.0.2.10, and <TOKEN>.
Final recommendation
Use STUN + Cloudflare 302 for a browser GET page shared with a small, trusted audience when exposing the resulting IP and port is acceptable and the NAT conditions are already proven. It solves “remember one stable entry address”; it is not a reverse proxy, security gateway, or true port-hiding mechanism.
If a request must preserve its method, consider 307 only after validating the client. Do not put 303 in the Single Redirect JSON. A required 303 should be generated by a Worker or origin with a separately secured store for the dynamic target.
Choose Cloudflare Tunnel for websites and APIs that should keep one standard HTTPS hostname and use Access or WAF. It does not depend on STUN and its public URL does not change with the NAT mapping.
Choose STUN when direct TCP/UDP behavior and a short path matter, your network satisfies Lucky's conditions, and you accept a changing public port, direct exposure of the origin through DNS-only records, and responsibility for authentication and firewall policy.
Direct STUN, a STUN + 302 entry point, and Cloudflare Tunnel can all make a private service reachable, but only the last option keeps application traffic inside Cloudflare's path. Decide the protocol, client behavior, and security boundary before copying a 301 request body.
References
- Lucky: STUN traversal
- Lucky: Cloudflare Tunnel
- Lucky: Dynamic DNS
- Referenced approach: Lucky STUN with a Cloudflare 301 redirect
- Cloudflare Single Redirect status codes and dynamic expressions
- Create a Single Redirect through the Cloudflare API
- Cloudflare Rulesets API
- Cloudflare 3xx status code semantics
- Cloudflare Tunnel overview
- Create a remotely managed Tunnel
- Tunnel routing and CNAME records
- Network ports supported by the Cloudflare proxy
- Tunnel Token permissions and rotation
- Tunnel TLS troubleshooting
Comments
Sign in to join the discussion
Your email remains private; only your display name appears.