@rexaray008/nodeproxy 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README copy.md +283 -0
- package/README.md +271 -62
- package/bin/cli.js +204 -5
- package/lib/cloudflare.js +222 -0
- package/lib/config.js +55 -25
- package/lib/pm2.js +102 -0
- package/lib/server.js +35 -22
- package/nodeproxy.config.json +30 -19
- package/package.json +15 -6
package/README copy.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# nodeproxy
|
|
2
|
+
|
|
3
|
+
A tiny, zero-dependency reverse proxy for Node.js ("a mini nginx") with built-in Cloudflare Tunnel support — so a project running on your own machine can be reached at a real domain, with no port forwarding and no static IP.
|
|
4
|
+
|
|
5
|
+
This README is a complete walkthrough from a fresh clone to a live public domain. Follow it top to bottom the first time; after that, day-to-day usage is a single command (Step 7).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What you'll end up with
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Internet --https--> Cloudflare edge --tunnel--> cloudflared (your machine) --> nodeproxy (port 8080) --> your project (e.g. port 3000)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Cloudflare terminates TLS and routes traffic through an outbound-only tunnel to `cloudflared` on your machine. `cloudflared` hands it to `nodeproxy`, which routes it to whichever local project you've configured. Nothing needs to be exposed on your router or firewall.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
Before you start, make sure you have:
|
|
22
|
+
|
|
23
|
+
- **Node.js 16 or newer** — check with `node -v`. Get it from https://nodejs.org if missing.
|
|
24
|
+
- **A project already running locally** that you want to expose — e.g. something listening on `http://localhost:3000`. It can be anything (Express, Next.js, a static site server, etc.) — nodeproxy just forwards to it.
|
|
25
|
+
- **(Only if using a real domain) A domain added to a Cloudflare account**, with its nameservers pointed at Cloudflare. The free plan is enough. If you just want to test locally without a public domain, skip Steps 5–7.
|
|
26
|
+
- **(Linux only, for auto-install) `curl` and `sudo`** available in your shell.
|
|
27
|
+
- **(macOS only, for auto-install) Homebrew** installed (https://brew.sh).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Step 1 — Get the files
|
|
32
|
+
|
|
33
|
+
Unzip the package and move into it:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
unzip nodeproxy.zip
|
|
37
|
+
cd nodeproxy
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Step 2 — Make the `nodeproxy` command available
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm link
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This registers `nodeproxy` as a global command pointing at this folder. Verify it worked:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
nodeproxy help
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You should see the full command list. (If you'd rather not touch global npm links, you can run everything below as `node bin/cli.js ...` instead of `nodeproxy ...` — they're equivalent.)
|
|
53
|
+
|
|
54
|
+
## Step 3 — Start the proxy for the first time
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
nodeproxy start
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Expected output:
|
|
61
|
+
```
|
|
62
|
+
[nodeproxy] Using config: /path/to/nodeproxy.config.json
|
|
63
|
+
[nodeproxy] (edit this file, or use "nodeproxy add <host> <target>", to change routing)
|
|
64
|
+
[nodeproxy] HTTP listening on port 8080
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
On this first run, nodeproxy auto-creates `nodeproxy.config.json` next to the CLI, defaulting to: listen on port 8080, forward everything to `http://localhost:3000`.
|
|
68
|
+
|
|
69
|
+
Leave this running and, in a second terminal, check it works:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
curl http://localhost:8080
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If your project is already running on port 3000, you'll see its response. If not, you'll get a `502 Bad Gateway` — that's expected until your project is up; it just means the proxy itself is working correctly.
|
|
76
|
+
|
|
77
|
+
Stop it for now with `Ctrl+C` — you'll restart it with the tunnel in Step 7.
|
|
78
|
+
|
|
79
|
+
## Step 4 — Point it at your actual project(s)
|
|
80
|
+
|
|
81
|
+
If your project runs on a port other than 3000, edit `nodeproxy.config.json` directly, or:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
nodeproxy add "*" http://localhost:YOUR_PORT
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Running more than one project?** Give each its own listener/port on the proxy side:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
nodeproxy listen 9090
|
|
91
|
+
nodeproxy add "*" http://localhost:4000 --port 9090
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Now port 8080 and port 9090 on the proxy are two completely independent routes. You can also route by domain name instead of `*` if a single port should serve multiple domains — see `nodeproxy.config.json` for the `routes` array format (each entry is `{ host, targets, staticDir }`).
|
|
95
|
+
|
|
96
|
+
Skip to Step 8 if you only want local routing and don't need a public domain yet.
|
|
97
|
+
|
|
98
|
+
## Step 5 — One-time Cloudflare login and tunnel creation
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
nodeproxy tunnel setup --domain app.yourdomain.com --port 8080
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Replace `app.yourdomain.com` with the actual subdomain you want live, and `8080` with whichever proxy port you want it to reach (from Step 4).
|
|
105
|
+
|
|
106
|
+
What happens, in order:
|
|
107
|
+
|
|
108
|
+
1. **cloudflared install check.** If missing, nodeproxy tries to install it automatically (Homebrew on macOS, direct binary download on Linux). If auto-install fails on your platform, it prints the exact manual install command and stops — just run that command and re-run `tunnel setup`.
|
|
109
|
+
2. **Browser login.** If this machine hasn't authorized with Cloudflare before, your browser opens to a Cloudflare page. Log in and click **Authorize**. This is the one manual step in the entire setup — it's how Cloudflare's OAuth flow works and can't be scripted by any tool. It only happens once per machine.
|
|
110
|
+
3. **Tunnel creation.** Creates a named tunnel (defaults to your domain with dots replaced by dashes, e.g. `app-yourdomain-com`).
|
|
111
|
+
4. **Ingress config.** Writes `~/.cloudflared/<name>.yml` mapping your domain to `http://localhost:8080` (or whichever port you passed).
|
|
112
|
+
5. **DNS routing.** Automatically adds the CNAME record in your Cloudflare DNS pointing your domain at the tunnel.
|
|
113
|
+
|
|
114
|
+
Expected final output:
|
|
115
|
+
```
|
|
116
|
+
[tunnel] Done! "app.yourdomain.com" now points at your local port 8080.
|
|
117
|
+
[tunnel] From now on, just run: nodeproxy start --tunnel --name app-yourdomain-com
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Step 6 — DNS propagation
|
|
121
|
+
|
|
122
|
+
Cloudflare DNS usually updates within seconds to a couple of minutes. You can check with:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
dig app.yourdomain.com
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
You should see it resolving to Cloudflare's IPs (not your own). This confirms DNS is correctly routed — the tunnel itself only carries traffic once it's actually running (next step).
|
|
129
|
+
|
|
130
|
+
## Step 7 — Go live (the one command you'll use from now on)
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
nodeproxy start --tunnel
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This single command starts both the local proxy and the Cloudflare tunnel together. Expected output:
|
|
137
|
+
```
|
|
138
|
+
[nodeproxy] Using config: /path/to/nodeproxy.config.json
|
|
139
|
+
[nodeproxy] HTTP listening on port 8080
|
|
140
|
+
[tunnel] Starting cloudflared tunnel "app-yourdomain-com"...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Visit `https://app.yourdomain.com` in a browser — it should now serve your local project. TLS is handled entirely by Cloudflare's edge, so `https://` works immediately with no certificates on your end.
|
|
144
|
+
|
|
145
|
+
If you created the tunnel with a custom `--name` in Step 5, pass it here too:
|
|
146
|
+
```bash
|
|
147
|
+
nodeproxy start --tunnel --name app2
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Step 8 — Verify everything end to end
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# 1. Confirm the local proxy responds
|
|
154
|
+
curl -H "Host: app.yourdomain.com" http://localhost:8080
|
|
155
|
+
|
|
156
|
+
# 2. Confirm the public domain responds the same way
|
|
157
|
+
curl https://app.yourdomain.com
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Both should return the same content from your backend project. If step 1 works but step 2 doesn't, the issue is in the tunnel/DNS layer (see Troubleshooting). If step 1 itself fails, the issue is your local project or the `add`/`listen` routing config, not Cloudflare.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Running multiple domains/projects through tunnels
|
|
165
|
+
|
|
166
|
+
Repeat Steps 4–7 per project, giving each a distinct proxy port and tunnel name:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Project A
|
|
170
|
+
nodeproxy listen 8080 # if not already there by default
|
|
171
|
+
nodeproxy add "*" http://localhost:3000 --port 8080
|
|
172
|
+
nodeproxy tunnel setup --domain appA.yourdomain.com --port 8080 --name appA
|
|
173
|
+
|
|
174
|
+
# Project B
|
|
175
|
+
nodeproxy listen 9090
|
|
176
|
+
nodeproxy add "*" http://localhost:4000 --port 9090
|
|
177
|
+
nodeproxy tunnel setup --domain appB.yourdomain.com --port 9090 --name appB
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Each tunnel needs its own `nodeproxy start --tunnel --name <name>` process, so run them in separate terminals (or as separate background services — see below).
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Keeping it running permanently (survive terminal close / reboot)
|
|
185
|
+
|
|
186
|
+
`nodeproxy start --tunnel` runs in the foreground and stops if you close the terminal. For a server you want always-on, run it as a background service.
|
|
187
|
+
|
|
188
|
+
**Linux (systemd)** — create `/etc/systemd/system/nodeproxy.service`:
|
|
189
|
+
```ini
|
|
190
|
+
[Unit]
|
|
191
|
+
Description=nodeproxy with Cloudflare Tunnel
|
|
192
|
+
After=network.target
|
|
193
|
+
|
|
194
|
+
[Service]
|
|
195
|
+
WorkingDirectory=/path/to/nodeproxy
|
|
196
|
+
ExecStart=/usr/bin/env nodeproxy start --tunnel
|
|
197
|
+
Restart=always
|
|
198
|
+
User=YOUR_USERNAME
|
|
199
|
+
|
|
200
|
+
[Install]
|
|
201
|
+
WantedBy=multi-user.target
|
|
202
|
+
```
|
|
203
|
+
Then:
|
|
204
|
+
```bash
|
|
205
|
+
sudo systemctl enable --now nodeproxy
|
|
206
|
+
sudo systemctl status nodeproxy
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**macOS (launchd)** or a process manager like `pm2`/`forever` work equally well if you prefer those — the command to keep alive is just `nodeproxy start --tunnel`.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Troubleshooting
|
|
214
|
+
|
|
215
|
+
| Symptom | Likely cause | Fix |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `502 Bad Gateway` from `curl localhost:8080` | Your local project isn't running, or is on a different port than configured | Start your project, or run `nodeproxy add "*" http://localhost:CORRECT_PORT` |
|
|
218
|
+
| `404 Not Found (no matching route...)` | The `Host` header doesn't match any route and there's no `*` catch-all on that listener | Add a catch-all: `nodeproxy add "*" http://localhost:PORT --port PROXY_PORT` |
|
|
219
|
+
| `tunnel setup` fails at install | Your platform isn't macOS/Linux auto-install, or missing `sudo`/Homebrew | Run the manual install command it printed, then re-run `tunnel setup` |
|
|
220
|
+
| Browser doesn't open during login | Headless/remote machine (e.g. SSH session) | `cloudflared tunnel login` prints a URL to the terminal too — open it manually on any device and authorize |
|
|
221
|
+
| `curl localhost:8080` works but the public domain doesn't | Tunnel isn't running, or DNS hasn't propagated yet | Confirm `nodeproxy start --tunnel` is actually running and check `dig yourdomain.com`; wait a minute and retry |
|
|
222
|
+
| "Port X already has a listener" | You tried `nodeproxy listen` on a port that's already configured | Use `nodeproxy add ... --port X` directly instead of `listen` |
|
|
223
|
+
| Changes to `nodeproxy.config.json` don't take effect | The proxy only reads the config at startup | Restart `nodeproxy start` (Ctrl+C, then run it again) |
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Command reference
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
nodeproxy start [--config <file>] [--tunnel] [--name <name>]
|
|
231
|
+
Start the proxy. --tunnel also starts
|
|
232
|
+
the Cloudflare tunnel alongside it.
|
|
233
|
+
nodeproxy init [--config <file>] Just create the config file.
|
|
234
|
+
nodeproxy add <host> <target> [--port <n>] [--config <file>]
|
|
235
|
+
Add/update a route on a listener.
|
|
236
|
+
<host> can be a real domain or "*".
|
|
237
|
+
nodeproxy listen <port> [--config <file>]
|
|
238
|
+
Open a new, independent listener on
|
|
239
|
+
a new port.
|
|
240
|
+
nodeproxy tunnel setup --domain <domain> [--name <name>] [--port <n>]
|
|
241
|
+
One-time: install cloudflared, log
|
|
242
|
+
in, create tunnel, route DNS.
|
|
243
|
+
nodeproxy tunnel start [--name <name>] Run just the tunnel (no local proxy).
|
|
244
|
+
nodeproxy help Show all commands.
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Config file reference (`nodeproxy.config.json`)
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"logging": true,
|
|
252
|
+
"servers": [
|
|
253
|
+
{
|
|
254
|
+
"port": 8080,
|
|
255
|
+
"https": { "enabled": false, "key": "", "cert": "" },
|
|
256
|
+
"routes": [
|
|
257
|
+
{ "host": "*", "targets": ["http://localhost:3000"], "staticDir": null }
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- `servers` — one entry per port the proxy listens on; each is fully independent.
|
|
265
|
+
- `routes[].host` — a real hostname, or `"*"` as a catch-all for that listener.
|
|
266
|
+
- `routes[].targets` — one or more backend URLs; 2+ enables automatic round-robin load balancing.
|
|
267
|
+
- `routes[].staticDir` — set this and leave `targets` empty to serve static files from a folder instead of proxying.
|
|
268
|
+
- `https` — only needed if you're terminating TLS yourself instead of using Cloudflare Tunnel (uncommon once you're using a tunnel, since Cloudflare's edge already handles TLS).
|
|
269
|
+
|
|
270
|
+
## Project layout
|
|
271
|
+
```
|
|
272
|
+
bin/cli.js CLI entrypoint
|
|
273
|
+
lib/server.js Core proxy engine (multi-listener routing, load balancing, static serving)
|
|
274
|
+
lib/config.js Config file loading/creation
|
|
275
|
+
lib/cloudflare.js Cloudflare Tunnel install/login/create/route/run helpers
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Known limitations
|
|
279
|
+
- No automatic Let's Encrypt renewal for self-managed HTTPS (not needed with Cloudflare Tunnel)
|
|
280
|
+
- One cert per listener if self-managing HTTPS (no per-domain SNI certs)
|
|
281
|
+
- No rate limiting, caching, or WAF
|
|
282
|
+
- Round-robin load balancing only
|
|
283
|
+
- The Cloudflare browser-login click is inherently manual and one-time per machine — this is Cloudflare's security design, not a gap in this tool
|
package/README.md
CHANGED
|
@@ -1,100 +1,309 @@
|
|
|
1
1
|
# nodeproxy
|
|
2
2
|
|
|
3
|
-
A tiny, zero-dependency reverse proxy
|
|
3
|
+
A tiny, zero-dependency reverse proxy for Node.js ("a mini nginx"), with:
|
|
4
|
+
- **pm2 integration** — nodeproxy can start and keep your own project alive, so you don't have to manually run it first
|
|
5
|
+
- **Cloudflare Tunnel integration** — expose your local machine on a real domain, no port forwarding, no static IP
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
- Host-based virtual routing (`api.example.com` → one backend, `app.example.com` → another)
|
|
7
|
-
- Round-robin load balancing across multiple upstream targets per host
|
|
8
|
-
- Wildcard `*` catch-all host for local dev
|
|
9
|
-
- Static file fallback (serve a folder directly, no backend needed)
|
|
10
|
-
- HTTPS support (bring your own cert/key)
|
|
11
|
-
- Zero npm dependencies — pure Node.js `http`/`https` core modules
|
|
7
|
+
This README is a complete walkthrough from a fresh clone to "my project is live at my domain, and it survives crashes/reboots." Follow it top to bottom the first time; after that, day-to-day usage is one command (Step 8).
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What you'll end up with
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Internet --https--> Cloudflare edge --tunnel--> cloudflared (your machine) --> nodeproxy (8080) --> pm2-managed project (3000)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
pm2 keeps your project running (and restarts it if it crashes). nodeproxy forwards traffic to it. cloudflared carries traffic to your machine with no exposed ports. One `nodeproxy start --tunnel` brings all three layers up together.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- **Node.js 16+** — check with `node -v`.
|
|
24
|
+
- **npm** — used to auto-install `pm2` if it's missing (no manual install needed).
|
|
25
|
+
- **(Only for a public domain) A domain on a Cloudflare account**, nameservers already pointed at Cloudflare. Free plan is fine.
|
|
26
|
+
- **(Linux) `curl` and `sudo`** for cloudflared auto-install. **(macOS) Homebrew.**
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Step 1 — Register your project so nodeproxy starts it for you
|
|
33
|
+
|
|
34
|
+
This is the part that removes the "start your project manually first" step. Point it at your project's start command:
|
|
14
35
|
|
|
15
36
|
```bash
|
|
16
|
-
|
|
37
|
+
nodeproxy app add myapp --cmd "npm start" --cwd /path/to/your/project
|
|
17
38
|
```
|
|
18
39
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
40
|
+
- `myapp` — any name you want, used for logs/status later
|
|
41
|
+
- `--cmd` — whatever normally boots your project (`npm start`, `node server.js`, `yarn dev`, etc.)
|
|
42
|
+
- `--cwd` — the folder that command should run from
|
|
22
43
|
|
|
23
|
-
|
|
44
|
+
This just registers it in `nodeproxy.config.json` — nothing starts yet. Repeat this for each project you have (see multi-project section below).
|
|
45
|
+
|
|
46
|
+
**Already running your project some other way (Docker, a different process manager, always-on already)?** Skip this step entirely — nodeproxy will just proxy to whatever's listening on the port, same as before.
|
|
47
|
+
|
|
48
|
+
## Step 2 — Point the proxy at it
|
|
49
|
+
|
|
50
|
+
By default nodeproxy forwards port 8080 → `http://localhost:3000`. If your project runs on a different port:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
nodeproxy add "*" http://localhost:YOUR_PORT
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Step 3 — First full local start
|
|
24
57
|
|
|
25
|
-
### Install globally instead
|
|
26
58
|
```bash
|
|
27
|
-
npm install -g nodeproxy
|
|
28
59
|
nodeproxy start
|
|
29
60
|
```
|
|
30
|
-
(publish this folder to npm first, or just `npm link` it locally — see below)
|
|
31
61
|
|
|
32
|
-
|
|
62
|
+
Expected output — notice pm2 starting your app automatically, no manual `npm start` needed:
|
|
63
|
+
```
|
|
64
|
+
[nodeproxy] Using config: /path/to/nodeproxy.config.json
|
|
65
|
+
[pm2] Starting "myapp" (npm start) in /path/to/your/project ...
|
|
66
|
+
[PM2] Done.
|
|
67
|
+
[nodeproxy] HTTP listening on port 8080
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Verify:
|
|
33
71
|
```bash
|
|
34
|
-
|
|
72
|
+
curl http://localhost:8080
|
|
35
73
|
```
|
|
74
|
+
You should see your project's actual response — even though you never typed `npm start` yourself.
|
|
36
75
|
|
|
37
|
-
|
|
76
|
+
Check on your app anytime with:
|
|
77
|
+
```bash
|
|
78
|
+
nodeproxy app list # status of every registered app
|
|
79
|
+
nodeproxy app logs myapp
|
|
80
|
+
nodeproxy app stop myapp
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Stop everything with `Ctrl+C` (nodeproxy) — your app keeps running under pm2 in the background even after that, exactly as pm2 is designed to. Use `nodeproxy app stop myapp` to actually stop it.
|
|
84
|
+
|
|
85
|
+
## Step 4 — One-time Cloudflare login and tunnel creation
|
|
86
|
+
|
|
87
|
+
Only needed if you want a real public domain (skip to Step 9 if you're only using this locally).
|
|
38
88
|
|
|
39
89
|
```bash
|
|
40
|
-
|
|
41
|
-
|
|
90
|
+
nodeproxy tunnel setup --domain app.yourdomain.com --port 8080
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
What happens, in order:
|
|
94
|
+
1. **cloudflared install check** — auto-installed if missing (Homebrew on macOS, direct binary on Linux; manual instructions printed if your platform needs it).
|
|
95
|
+
2. **Browser login** — opens once so you can click **Authorize** in your Cloudflare account. This is the *only* manual step in the whole setup; it's how Cloudflare's OAuth login works and cannot be scripted by any tool, including Cloudflare's own CLI.
|
|
96
|
+
3. **Tunnel creation** — a named tunnel is created for you.
|
|
97
|
+
4. **Ingress config** — written to `~/.cloudflared/<name>.yml`, mapping your domain to `http://localhost:8080`.
|
|
98
|
+
5. **DNS routing** — the CNAME record is added to Cloudflare DNS automatically.
|
|
42
99
|
|
|
43
|
-
|
|
44
|
-
|
|
100
|
+
Expected output:
|
|
101
|
+
```
|
|
102
|
+
[tunnel] Done! "app.yourdomain.com" now points at your local port 8080.
|
|
103
|
+
[tunnel] From now on, just run: nodeproxy start --tunnel --name app-yourdomain-com
|
|
45
104
|
```
|
|
46
105
|
|
|
47
|
-
|
|
106
|
+
## Step 5 — Check DNS propagated
|
|
48
107
|
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
"port": 8080,
|
|
52
|
-
"https": { "enabled": false, "port": 8443, "key": "", "cert": "" },
|
|
53
|
-
"logging": true,
|
|
54
|
-
"routes": [
|
|
55
|
-
{ "host": "app.example.com", "targets": ["http://localhost:4000", "http://localhost:4001"], "staticDir": null },
|
|
56
|
-
{ "host": "static.example.com", "targets": [], "staticDir": "./public" },
|
|
57
|
-
{ "host": "*", "targets": ["http://localhost:3000"], "staticDir": null }
|
|
58
|
-
]
|
|
59
|
-
}
|
|
108
|
+
```bash
|
|
109
|
+
dig app.yourdomain.com
|
|
60
110
|
```
|
|
111
|
+
Should resolve to Cloudflare IPs within a minute or so.
|
|
61
112
|
|
|
62
|
-
|
|
63
|
-
- `staticDir` set + empty `targets` = pure static file server for that host
|
|
64
|
-
- `host: "*"` = catch-all, used when no other host matches (handy for local dev where you don't care about the `Host` header)
|
|
113
|
+
## Step 6 — **Go live: the one command you'll use from now on**
|
|
65
114
|
|
|
66
|
-
|
|
115
|
+
```bash
|
|
116
|
+
nodeproxy start --tunnel
|
|
117
|
+
```
|
|
67
118
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
119
|
+
This starts, in order: your registered app(s) via pm2 → the local proxy → the Cloudflare tunnel. Visit `https://app.yourdomain.com` — it should serve your project. TLS is handled by Cloudflare's edge automatically.
|
|
120
|
+
|
|
121
|
+
## Step 7 — Verify from more than one place
|
|
122
|
+
|
|
123
|
+
This matters — testing only from the machine running nodeproxy can hide DNS problems:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# From the same machine — hits the local proxy directly, bypassing Cloudflare entirely
|
|
127
|
+
curl -H "Host: app.yourdomain.com" http://localhost:8080
|
|
128
|
+
|
|
129
|
+
# From the same machine — the real public path, through Cloudflare
|
|
130
|
+
curl https://app.yourdomain.com
|
|
131
|
+
|
|
132
|
+
# From a phone on cellular data, or ask a friend to open it
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
If the first two work but a different device still can't reach it, jump straight to the "works on my machine, not on other devices" section below.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## "It works on my machine but not on another device/network"
|
|
140
|
+
|
|
141
|
+
This is almost always **one specific cause**, not a bug in the proxy: the DNS record Cloudflare created for your tunnel is set to **"DNS only" (grey cloud)** instead of **"Proxied" (orange cloud)**. Tunnel hostnames (`*.cfargotunnel.com`) only resolve *publicly* when proxied — when they're not, they can fail to resolve for everyone except a device that happens to have cached/local name resolution, which is exactly what makes it look like "works for me, not for anyone else."
|
|
142
|
+
|
|
143
|
+
Diagnose it directly instead of guessing:
|
|
144
|
+
```bash
|
|
145
|
+
nodeproxy tunnel doctor --domain app.yourdomain.com
|
|
75
146
|
```
|
|
76
147
|
|
|
77
|
-
|
|
148
|
+
This checks whether your domain actually resolves to a public IP address (not just whether a DNS record exists) and tells you exactly what's wrong. Example output when it's broken:
|
|
149
|
+
```
|
|
150
|
+
✗ Does NOT resolve to an IP address publicly — ENOTFOUND. This is almost always
|
|
151
|
+
because the DNS record is set to "DNS only" (grey cloud) in the Cloudflare
|
|
152
|
+
dashboard instead of "Proxied" (orange cloud). Fix: Cloudflare dashboard →
|
|
153
|
+
DNS → click the cloud icon next to this record so it turns orange, then
|
|
154
|
+
wait ~1 minute and retry.
|
|
155
|
+
```
|
|
78
156
|
|
|
79
|
-
|
|
157
|
+
**The fix:** log into the Cloudflare dashboard → your domain → DNS → find the record for your subdomain → click the cloud icon next to it so it turns **orange** (Proxied) → wait about a minute → retest.
|
|
158
|
+
|
|
159
|
+
Other, less common causes if the doctor says DNS is fine but it still doesn't work elsewhere:
|
|
160
|
+
- `nodeproxy start --tunnel` isn't actually running at that moment — the tunnel only carries traffic while that process is alive.
|
|
161
|
+
- You're testing from a network that blocks outbound HTTPS or has its own DNS overrides (rare, mostly corporate networks).
|
|
162
|
+
- You created more than one tunnel for the same domain by accident — run `cloudflared tunnel list` and delete stale ones.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Running more than one project
|
|
167
|
+
|
|
168
|
+
Each project needs: its own pm2 app registration, its own proxy port, and (if public) its own tunnel.
|
|
80
169
|
|
|
81
170
|
```bash
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
171
|
+
# Project A — port 8080
|
|
172
|
+
nodeproxy app add appA --cmd "npm start" --cwd /path/to/project-a
|
|
173
|
+
nodeproxy add "*" http://localhost:3000 # project A's actual listen port
|
|
174
|
+
nodeproxy tunnel setup --domain appA.yourdomain.com --port 8080 --name appA
|
|
175
|
+
|
|
176
|
+
# Project B — new port 9090
|
|
177
|
+
nodeproxy app add appB --cmd "npm start" --cwd /path/to/project-b
|
|
178
|
+
nodeproxy listen 9090
|
|
179
|
+
nodeproxy add "*" http://localhost:4000 --port 9090 # project B's actual listen port
|
|
180
|
+
nodeproxy tunnel setup --domain appB.yourdomain.com --port 9090 --name appB
|
|
85
181
|
```
|
|
86
182
|
|
|
87
|
-
|
|
183
|
+
Run each tunnel in its own terminal (or background service — see below):
|
|
184
|
+
```bash
|
|
185
|
+
nodeproxy start --tunnel --name appA
|
|
186
|
+
nodeproxy start --tunnel --name appB
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Making it survive a reboot / terminal close
|
|
192
|
+
|
|
193
|
+
`nodeproxy start --tunnel` runs in the foreground. Your pm2-managed app itself will actually survive that terminal closing (pm2 daemonizes it), but the proxy and tunnel won't unless you run one of these:
|
|
194
|
+
|
|
195
|
+
**pm2 boot persistence** (for your app surviving a full machine reboot):
|
|
196
|
+
```bash
|
|
197
|
+
pm2 startup # prints a sudo command — copy/run it, once
|
|
198
|
+
pm2 save # nodeproxy already runs this for you after "start"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**systemd (Linux)** for the proxy+tunnel — create `/etc/systemd/system/nodeproxy.service`:
|
|
202
|
+
```ini
|
|
203
|
+
[Unit]
|
|
204
|
+
Description=nodeproxy with Cloudflare Tunnel
|
|
205
|
+
After=network.target
|
|
206
|
+
|
|
207
|
+
[Service]
|
|
208
|
+
WorkingDirectory=/path/to/nodeproxy
|
|
209
|
+
ExecStart=/usr/bin/env nodeproxy start --tunnel
|
|
210
|
+
Restart=always
|
|
211
|
+
User=YOUR_USERNAME
|
|
212
|
+
|
|
213
|
+
[Install]
|
|
214
|
+
WantedBy=multi-user.target
|
|
88
215
|
```
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
lib/config.js Config file loading/creation
|
|
216
|
+
```bash
|
|
217
|
+
sudo systemctl enable --now nodeproxy
|
|
92
218
|
```
|
|
93
219
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
220
|
+
macOS: use `launchd`, or just keep `nodeproxy start --tunnel` itself under pm2 too (`pm2 start "nodeproxy start --tunnel" --name gateway`).
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Troubleshooting
|
|
225
|
+
|
|
226
|
+
| Symptom | Likely cause | Fix |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
| `502 Bad Gateway` locally | Your app isn't running, wrong port, or pm2 failed to start it | `nodeproxy app list` to check status; `nodeproxy app logs myapp` for errors |
|
|
229
|
+
| `404 Not Found (no matching route...)` | No `*` catch-all or matching host on that listener | `nodeproxy add "*" http://localhost:PORT --port PROXY_PORT` |
|
|
230
|
+
| pm2 app shows `errored` in `app list` | The `--cmd`/`--cwd` you registered doesn't actually start the app | Test the exact command manually in that folder first, then re-run `nodeproxy app add` |
|
|
231
|
+
| Works locally, not on another device | DNS record not proxied (grey cloud) | `nodeproxy tunnel doctor --domain yourdomain.com` — see dedicated section above |
|
|
232
|
+
| `tunnel setup` fails at install | Platform isn't auto-installable, or missing sudo/Homebrew | Run the manual command it prints, then retry |
|
|
233
|
+
| Browser doesn't open during login | Headless/SSH session | `cloudflared tunnel login` also prints a URL — open it on any device |
|
|
234
|
+
| Config changes don't take effect | Config is only read at startup | Restart `nodeproxy start` |
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Command reference
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
nodeproxy start [--config <file>] [--tunnel] [--name <name>]
|
|
242
|
+
Starts registered apps (pm2), then
|
|
243
|
+
the proxy, then the tunnel if
|
|
244
|
+
--tunnel is given. The one command.
|
|
245
|
+
nodeproxy init [--config <file>] Just create the config file.
|
|
246
|
+
nodeproxy add <host> <target> [--port <n>] [--config <file>]
|
|
247
|
+
Add/update a proxy route.
|
|
248
|
+
nodeproxy listen <port> [--config <file>]
|
|
249
|
+
Open a new independent listener.
|
|
250
|
+
|
|
251
|
+
nodeproxy app add <name> --cmd "<cmd>" [--cwd <dir>]
|
|
252
|
+
Register a project to be started
|
|
253
|
+
and kept alive via pm2.
|
|
254
|
+
nodeproxy app list Show registered apps + live status.
|
|
255
|
+
nodeproxy app logs <name> Tail an app's logs.
|
|
256
|
+
nodeproxy app stop <name> Stop an app.
|
|
257
|
+
|
|
258
|
+
nodeproxy tunnel setup --domain <domain> [--name <name>] [--port <n>]
|
|
259
|
+
One-time: install, log in, create
|
|
260
|
+
tunnel, route DNS.
|
|
261
|
+
nodeproxy tunnel start [--name <name>] Run just the tunnel.
|
|
262
|
+
nodeproxy tunnel doctor --domain <domain>
|
|
263
|
+
Diagnose public-access DNS issues.
|
|
264
|
+
|
|
265
|
+
nodeproxy help Show all commands.
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Config file reference (`nodeproxy.config.json`)
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"logging": true,
|
|
273
|
+
"apps": [
|
|
274
|
+
{ "name": "myapp", "cmd": "npm start", "cwd": "/path/to/project" }
|
|
275
|
+
],
|
|
276
|
+
"servers": [
|
|
277
|
+
{
|
|
278
|
+
"port": 8080,
|
|
279
|
+
"https": { "enabled": false, "key": "", "cert": "" },
|
|
280
|
+
"routes": [
|
|
281
|
+
{ "host": "*", "targets": ["http://localhost:3000"], "staticDir": null }
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
]
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
- `apps` — projects nodeproxy starts/keeps alive via pm2. Optional.
|
|
289
|
+
- `servers` — one entry per proxy port; each fully independent.
|
|
290
|
+
- `routes[].host` — real hostname or `"*"` catch-all.
|
|
291
|
+
- `routes[].targets` — 2+ enables round-robin load balancing.
|
|
292
|
+
- `routes[].staticDir` — serve static files instead of proxying.
|
|
293
|
+
- `https` — only for self-managed TLS; not needed with Cloudflare Tunnel.
|
|
294
|
+
|
|
295
|
+
## Project layout
|
|
296
|
+
```
|
|
297
|
+
bin/cli.js CLI entrypoint
|
|
298
|
+
lib/server.js Core proxy engine
|
|
299
|
+
lib/config.js Config loading/creation
|
|
300
|
+
lib/cloudflare.js Cloudflare Tunnel install/login/create/route/run/diagnose
|
|
301
|
+
lib/pm2.js pm2 install/start/stop/list helpers
|
|
302
|
+
```
|
|
99
303
|
|
|
100
|
-
|
|
304
|
+
## Known limitations
|
|
305
|
+
- No automatic Let's Encrypt renewal for self-managed HTTPS (not needed with Cloudflare Tunnel)
|
|
306
|
+
- Round-robin load balancing only
|
|
307
|
+
- The Cloudflare browser-login click is inherently manual and one-time per machine — Cloudflare's security design, not a gap here
|
|
308
|
+
- `tunnel doctor` diagnoses the most common public-access failure (unproxied DNS); it isn't an exhaustive network debugger
|
|
309
|
+
|