Switch from ngrok to qURL
Checked against the qURL™ CLI 3.4.1 source and documentation on October 14, 2026. Link and session behaviour is tested separately.
Content updated October 18, 2026. The verification date above records the original review; later behaviour checks are dated in the guide.
What is the qURL command for ngrok http 3000?
Replace ngrok http 3000 with qurl publish http://127.0.0.1:3000, then run qurl share <CRID> for each person you send the app to. There is no account or authtoken, no public URL, and no warning page, and WebSockets and Server-Sent Events pass through. Keep ngrok for webhooks, public APIs, raw TCP, and request inspection and replay.
What you need
- An HTTP app running on this machine that you shared with ngrok, for example on port 3000
- The qURL CLI 3.4.1 or later
- No account, API key, or authtoken
Steps
Map your ngrok commands
ngrok http 3000 gives the app a public URL on your account’s dev domain (ngrok-free.app or ngrok-free.dev) for as long as the agent runs. qurl publish takes the app’s full local address and gives it no public URL; qurl share then makes a link for one recipient. The authtoken step has no counterpart: the first publish creates this device’s identity.
# ngrok # qURL ngrok config add-authtoken <TOKEN> # nothing to do: no account ngrok http 3000 # qurl publish http://127.0.0.1:3000 # qurl share <CRID> (one link per person) stop the ngrok agent # qurl stop <CRID>, or qurl delete <CRID>Install the qURL CLI
On macOS, install it with Homebrew. On Linux and Windows, download the package for your platform from the latest release; the CLI page lists each one. Nothing asks you to sign up.
brew install layervai/tap/qurl qurl --versionPublish the app
The first publish creates this device’s identity, starts a background service that keeps serving after you close the terminal (while you stay logged in), and prints the app’s CRID, its stable ID. The Access row says who can open the app: a plain publish makes it private from qurl 3.4.1 on. qurl share turns the CRID into an access link, and the link is what you send.
qurl publish http://127.0.0.1:3000Published Target: http://127.0.0.1:3000 Access: private — only you and the people you allow can open it Status: serving CRID: ae4jqpd7eaoslq7jinmjv4yikgzmcxgpjfsuobiniqnko32lpw743ivbeyha
Create a link for each person
qurl share prints an access link (https://qurl.link/#…). On a terminal, the lines below it say who published the resource and when the link expires, for example “Expires in 1d (single use)”. Today LayerV has verified only its own account, so for every other publisher the Publisher line says UNVERIFIED. With no flags the link can be opened once and expires within 24 hours. If the expiry line does not say “single use”, don’t send the link. Add --ttl 1h for a shorter window.
qurl share <CRID>Allow qurl.site in your dev server
Requests through a qURL link arrive with a qurl.site host. If you added .ngrok-free.app to Vite’s server.allowedHosts or *.ngrok-free.app to Next.js allowedDevOrigins for the tunnel, add .qurl.site and *.qurl.site beside it and restart the dev server. Leave off --host: qURL reaches the dev server on localhost.
// vite.config.js (merge into your existing config) export default { server: { allowedHosts: ['.qurl.site'] }, } // next.config.js module.exports = { allowedDevOrigins: ['*.qurl.site'], }Send the link to one person
Send it in a direct message, not a public channel: whoever opens it first uses it. Avoid email and apps that preview links, because a scanner that runs the page could use its one opening. Don’t open it yourself to check it, for the same reason. Run qurl share again whenever you need a fresh link.
What the recipient sees
The recipient clicks the link and lands on your app at an address like https://r_….qurl.site/. There is no ERR_NGROK_6024 warning page, sign-up, or install. Their session lasts 1 hour by default, and your app’s own login still applies if it has one.
Troubleshooting
| If you see | Do this |
|---|---|
| A webhook sender, such as Stripe or GitHub, can’t reach the app | qURL is not built for webhooks: a link has to be opened by a browser or a qURL SDK opener before requests reach the app, and webhook senders do neither. Keep a public endpoint for that sender. |
| You need to see and replay the requests the app received | qURL has no request inspector. ngrok’s web interface on localhost:4040 shows every request and response and replays a request with one click; keep ngrok for that kind of debugging. |
| qurl publish says the local app is not accepting TCP connections | Start the app first and check that curl works on the same address. qurl publish connects to localhost as 127.0.0.1, so if your app listens only on IPv6 (on macOS or Linux, lsof -iTCP -sTCP:LISTEN -nP shows [::1]), publish 'http://[::1]:PORT' instead. Keep the quotes: zsh treats the brackets as a pattern. |
| The recipient sees “Access link invalid” | The link was already used or has expired. Run qurl share again and send the new link. |
| The recipient sees “Resource not found” | The resource was deleted after the link was made. Publish the app again, then run qurl share and send the new link. |
| publish rejects http://127.0.0.1 with “only HTTPS URLs are allowed”, or asks you to log in | You have a CLI older than 3.0. On macOS run brew update, then brew upgrade qurl; elsewhere install the latest release again. After updating, qurl --version should print 3.4.1 or later. |
| publish prints no Access: line, or the Access: line says public and you did not ask for public | With no Access: line, the CLI is older than 3.4.1: on macOS run brew update, then brew upgrade qurl; elsewhere install the latest release again. A resource that an older CLI published is public unless you published it with --private, and anyone who has the CRID of a public resource can open it. To make a public resource private, run qurl delete <CRID> and publish again with qurl 3.4.1 or later. The new resource is private and has a new CRID. |
| publish says this Linux session “can’t reach a systemd user manager” | You are in a container or sandbox without a user service manager. Run qurl publish with --foreground; sharing lasts as long as that process runs. |
| publish says “Quota Exceeded” with “protected resource limit reached”, or “Your qURL account has reached a plan limit” | You can see this without an account: a device with no account can keep up to 3 active resources, with links that last up to 24 hours. Run qurl list --status active, then qurl delete <CRID> for any you no longer need (it asks you to confirm; --yes skips that). qurl stop keeps the resource, so it does not free a slot. |
Frequently Asked Questions
What carries over from ngrok?
Any local HTTP app. WebSocket upgrades and Server-Sent Events pass through a qURL link, and in our test Vite’s hot reload kept working once server.allowedHosts included .qurl.site. The app keeps serving after you close the terminal, because qurl publish starts a background service.
When should I keep ngrok?
When something other than a person or agent you hand a link to must reach the app: a webhook from Stripe or GitHub, a public API anyone can call, or raw TCP (on ngrok’s free plan, TCP needs card verification). Keep it too for request inspection and replay on localhost:4040, custom domains on paid plans, or a Traffic Policy that puts OAuth or OIDC login in front of the endpoint.
Can several people use one link, the way they share my ngrok URL?
Not with qurl share: links for apps published from the CLI are single-use by default, and it has no option for a reusable link. Run qurl share once per person, so each link expires on its own. For people who come back over days, invite them by email through the qURL skill instead; the contractor guide shows how.
Do I still need my ngrok authtoken?
Not for qURL. The first qurl publish creates a device identity on your machine; there is no account, authtoken, or API key. Without an account you can keep up to 3 active resources, with links that last up to 24 hours.
Who else can reach my app while the link is open?
Opening the link admits the network address it was opened from for the session, which lasts 1 hour by default. Other devices that share that public IP address, such as people on the same office or café network or VPN, can reach the app until the session ends. By default a link admits whoever opens it; access rules set through the API, such as IP allow and deny lists, can restrict that. Keep your app’s own sign-in for anything sensitive.
How do I stop sharing?
Run qurl stop <CRID> to pause serving the app, and qurl start <CRID> to resume it; a stopped app still counts toward your resource limit. qurl delete <CRID> removes the resource for good after asking you to confirm; its CRID can never be shared again.