你的应用跑起来了。你启动了开发服务器,打开 http://localhost:3000,它也按预期工作。然后有人问你要一个链接,你才发现屏幕上的这个 URL 对除你之外的任何人都毫无意义。
在没有 VPS 的情况下,给 localhost 应用一个公开 URL 有三种方法,另外当对方就在你的本地网络里时还有一个更快的选项。在它们之间做选择并不是工具问题,而是这个应用需要保持可访问多久、以及它能否在你的笔记本之外运行的问题。下面逐一介绍每条路径、获得 URL 的命令,以及会让这个 URL 失效的确切原因。
TL;DR(太长不看版)
- 同一 Wi-Fi 下的人需要看: 把开发服务器绑定到所有网络接口,然后把你的局域网 IP 交出去。几秒钟就绪,访客一离开你的网络就失效。
- 你需要一个接下来一小时内任何人都能打开的链接: 运行一条隧道(
cloudflared、ngrok、localtunnel、localhost.run)。大约一分钟就能得到公开的 HTTPS URL,无需改动路由器,但它会随着启动它的进程一起消失。 - 笔记本合上后它也得继续在线: 把应用推到免费托管套餐上。这样应用就与你的笔记本脱钩了,但也带来了新规则:信用卡、商业用途,以及你的数据能否在重启后保留。
- 你的应用在请求时不需要服务器代码: 构建它,把静态产物放到静态托管上。它可以在没有你笔记本的情况下保持在线,也没有需要唤醒的应用进程,只要托管方的账户和用量限制仍然合适。
- 一个值得知道的默认行为:
next dev和python -m http.server在不加任何参数的情况下,就已经在监听所有网络接口。如果你以为开发服务器只对自己的笔记本可见,那你想错了。
哪条路径适合你的应用
这四条路径中有三条能给你的应用一个公开的互联网 URL;第一条只能到达你自己的网络,这让它既最快也最受限。按 URL 需要存活多久来排序,选择基本就自然明了了。
| 路径 | 获得 URL 所需时间 | 能持续多久 | 什么会让它失效 | 适合谁 |
|---|---|---|---|---|
| 同一网络 | 几秒钟 | 只要你们都在这个网络里 | 访客换到了另一个 Wi-Fi | 隔壁工位的同事,或者你自己的手机 |
| 隧道 | 大约一分钟 | 只要进程在运行 | 合上笔记本、关掉终端、触到套餐上限 | 一次演示、一次客户讲解、一次 webhook 测试 |
| 免费托管套餐 | 10 到 30 分钟 | 无限期,但有条件 | 闲置休眠、临时文件系统,或套餐条款 | 在你睡觉时也必须响应的东西 |
| 静态构建 | 10 到 20 分钟 | 无限期 | 在请求时需要服务端代码 | 能在构建时完整生成、或在客户端运行的应用 |
哪些行对你开放,取决于三件你可以在自己项目里检查的事:
- 应用在请求时需要运行你的服务器代码吗? 一个 Flask 或 FastAPI 路由、一个
server.js端点,或任何随请求变化的服务器逻辑,都需要服务端托管。构建时的服务器代码并不自动排除静态部署:Next.js 的 Server Components 可以在next build期间运行,静态的GETRoute Handler 也可以预渲染。如果运行时的每个请求都能作为静态资源提供,或者直接从浏览器发送到外部 API,静态路径就仍然可行。 - 它是否读写需要保留的文件? 数据库文件(
.db,.sqlite)、上传目录、它会编辑的 JSON 文件。如果有,部署前先查看托管方的存储模型。Render 的免费 Web 服务和 Koyeb 的免费实例使用临时本地存储,而 Vercel Functions 的文件系统是只读的,只有临时的/tmp空间。把持久状态放进持久卷、数据库或对象存储,而不是假设应用的本地磁盘能保留下来。 - 它在运行时需要密钥吗? 前两条路径中,
.env文件里的密钥可以原样使用,因为应用仍然运行在你的机器上。另外两条路径则需要你在服务商的环境设置里重新填写,而且它绝不能出现在你推送的仓库里。
在你自己的网络里分享
next dev 已经在监听你机器上的所有网络接口(你看到的 0.0.0.0 就是这个意思), python -m http.server也一样。两者都不需要参数,所以你此刻正在运行的开发服务器,很可能已经能从同一 Wi-Fi 下的手机访问了。
Next.js 的文档把 -H 记为修改这个主机名的方式, 默认值为 0.0.0.0;Python 的文档则说该模块会 绑定到所有接口 ,除非你传入 --bind 127.0.0.1。这让它成为分享 localhost 应用最快的方式:不用账号、不用安装、不用部署。其他常见的开发服务器则需要明确告知。
# Already listening on all interfaces. Nothing to add.
next dev
python -m http.server 8000
streamlit run app.py
# Needs the flag.
npm run dev -- --host # Vite
flask run --host=0.0.0.0
uvicorn main:app --host 0.0.0.0 # FastAPI
Vite 的文档: server.host 的默认值是 localhost,并且在命令行接受 --host ,或在配置文件中接受 server.host: '0.0.0.0' 。 Uvicorn 的默认值是 127.0.0.1,这也覆盖了 FastAPI,因为 FastAPI 正是由它运行的。Streamlit 把 server.address 留空,其配置参考指出 设置它会限制访问 ,只允许那一个地址:不设置就意味着不限制。
接下来你需要一个可以交出去的地址。那是你机器在本地网络上的自身 IP,而不是 localhost:
# macOS
ipconfig getifaddr en0
# Linux
hostname -I
# Windows (PowerShell)
ipconfig | findstr IPv4
把 http://<that-address>:3000 交给访客,他们就能进来了。问题出在整条路径的形态上:这个地址在你的网络之外没有任何意义。一旦对方换到另一个 Wi-Fi、切到蜂窝网络或者回到家,这个链接对他们来说就失效了。
注意: 如果命令运行正常,另一台设备却仍然连不上,几乎每次都是操作系统的防火墙在作怪,而不是命令本身。Apple 的防火墙文档说,macOS 会对尚未允许的应用弹出提示,并 在你处理之前拒绝连接。Windows 则会询问适用哪个网络配置文件,并为 专用网络和公用网络保留各自独立的规则 。在家庭或办公室网络里选「专用」。永远不要选「公用」。
把它放到隧道后面
隧道是一个和你的应用并排运行的小程序,它会给应用一个公开的 HTTPS 地址。一条命令就能在大约一分钟内获得通往 localhost 的免费隧道,但你需要提前知道:这条命令一停,URL 就跟着消失:
cloudflared tunnel --url http://localhost:3000
你的路由器不需要任何改动,原因在于连接的方向。你的机器向服务商的边缘节点发起一个出站连接,和浏览器加载任何网页时发起的连接是同一种,服务商保持这个连接打开,并把入站请求顺着它推回来。你这一侧的入站端口始终关闭,所以它在酒店 Wi-Fi、手机热点,以及你无法控制路由器的家庭网络上都能用。
注意: 最后这种情况值得你花六十秒检查一下,再考虑改用端口转发。打开路由器的状态页面,找到它报告的 WAN IP,再用任意「我的 IP 是多少」服务查一下你真实的公网 IP 并作比较。如果 WAN IP 落在
100.64.0.0/10范围内,那很可能是 CGNAT。如果 WAN IP 和公网 IP 只是不一致,你能确定上游还有一层 NAT,但那可能是 CGNAT,也可能是普通的双重 NAT。无论哪种情况,只在这台路由器上做端口转发都可能不够。这个地址段 由 RFC 6598 预留为共享地址空间,也就是运营商在地址用尽时把你放到其后面的那个地址段。
各个选项的差别主要在于它们一开始向你要什么。
Cloudflare 快速隧道 就是上面那条命令:不用账号、不用域名,会得到一个随机的 trycloudflare.com 子域名。Cloudflare 将其限制为 200 个并发请求,超出则返回 429,不支持 Server-Sent Events,并在同一份文档中说明免费隧道只用于测试和开发,不用于部署生产网站。
ngrok 需要先注册,然后运行 ngrok http 3000. 当前的免费套餐 提供 5 美元的一次性包含用量(不会按月刷新)、最多 3 个在线端点、1 GB 流量、20,000 次 HTTP/S 请求,以及一个访客必须点击通过的中间警告页。你还会得到一个自动分配的免费开发域名,这是 ngrok 在 2023 年宣布的 ,目的就是解决每次重启 URL 都会变的老问题。
localtunnel 不需要注册,除了 npx 之外也不需要安装任何东西: npx localtunnel --port 3000。你会得到一个随机子域名,README 明确说明 --subdomain 只是申请一个名字,并不保证能拿到.
localhost.run 什么都不用安装,因为它用的是你操作系统自带的 SSH 客户端: ssh -R 80:localhost:3000 localhost.run。它的文档指出 无需下载 ,免费域名也不需要设置账号。
VS Code 在 Ports 面板里提供了这个功能,如果你本来就常驻编辑器,会很顺手。它需要用 GitHub 或 Microsoft 账号登录,而默认设置会让你栽跟头: 转发的端口默认是私有的,这意味着在你把端口切换为公开之前,访客会被要求用你的账号登录。(给队友用没问题。给只想点个链接的客户用则毫无用处。)
Tailscale Funnel 也能做到,但有两个通常起决定作用的限制:URL 只能放在你自己的 tailnet 域名下,而且它只能 监听 443、8443 和 10000 端口.
无论选哪个,都要清楚自己交出去的是什么。对于没有访问控制的公开隧道,开发服务器提供的一切都能被任何持有该 URL 的人访问到,包括你从未链接过的路由,以及你忘了关掉的调试界面。用于一场十五分钟的演示没问题。贴到公开 Discord 里的 URL 就远没那么合适了。
过期之所以让人措手不及,是因为它看起来像应用坏了。这里大多数快捷方案仍然依赖运行在你笔记本上的隧道软件:停掉 cloudflared、ngrok、localtunnel 或 localhost.run 的 SSH 会话,转发就停止了。Tailscale Funnel 是个例外,如果你用 --bg运行它,Funnel 配置会在后台持续运行,并在重启后恢复。但只要笔记本本身离线,这些方案都无法继续提供你的本地应用。你可以让隧道连续运行好几天,它会一直工作,直到合上盖子或触到请求上限为止。
隧道是演示的正确工具,却是托管的错误工具:它的在线时长就是你笔记本的在线时长。
让应用离开你的机器
这是第一条你的笔记本不再是承重墙的路径,也是第一条条款比工具更重要的路径。在这里让一切结束的不是计时器,而是闲置休眠、文件系统重置,或者套餐方判定你的应用不是他们想放在免费实例上的那种东西。
下面的选项从长期免费套餐到短期试用都有。有些能在限额内让应用无限期在线;有些则在固定试用期后停止,或者需要付费账户才能获得算力。部署前先查清计费、休眠和存储规则。
下面的免费套餐条款,已于 2026 年 9 月 7 日对照各服务商自己的定价或文档页面核对过。
| 提供商 | 商业用途? | 数据能在重启后保留吗? | 坑在哪 |
|---|---|---|---|
| Netlify | 允许 | 能,配合 Netlify Blobs 或 Database | 无需绑卡即可开始;用完 Free 套餐的当月额度后,项目会暂停到下一个计费周期,除非升级 |
| Render | 未说明 | 重启即丢失 | 无需绑卡即可开始;闲置 15 分钟后休眠;免费 Postgres 在创建 30 天后过期 |
| Cloudflare Pages / Workers | 未说明 | 能,配合 KV、D1、R2 或 Durable Objects | Pages 每月 500 次构建;Workers 免费版为 每天 100,000 次请求 |
| Vercel | Hobby 不允许 | 重启即丢失 | Hobby 仅限个人用途;函数的文件系统为只读 |
| GitHub Pages | 不允许 | 仅限静态产物 | 不得用于在线业务、电商或商业 SaaS;部署超时为 10 分钟 |
| PythonAnywhere | 未说明 | 是 | 免费账户只能访问白名单内的外部主机; 免费 Web 应用一个月后过期 ,除非续期 |
| Fly.io | 未说明 | 能,配合 Fly Volume | 没有长期免费套餐:2 个机器小时或 7 天;试用 Machine 运行 5 分钟后自动停止,试用包含 20 GB 卷存储;试用结束后应用会停止,直到你添加银行卡 |
| Koyeb | 未说明 | 没有持久的本地存储 | 必须绑卡;Koyeb 会做一笔 29 美元的预授权并随即取消,但注册时默认选中 Pro,并按比例收取其费用,除非你切换到 Starter |
| Hugging Face Spaces | 未说明 | 重启即丢失 | Static Spaces 免费;Gradio 和 Docker 通常需要付费套餐,但符合条件的免费个人账户可以在 ZeroGPU 上托管最多两个 Gradio Space |
| Railway | 未说明 | 能,前提是应用使用附带的 0.5 GB 卷 | 30 天试用,含一次性 5 美元额度,之后转为 0 美元的 Free 套餐,每月 1 美元资源额度;无需绑卡 |
「未说明」指服务商自己的页面没有就其免费套餐回答这个问题。请把它当作未知,而不是「是」或「否」。
其中三行在开始前值得再多看一眼。Fly.io 没有长期免费套餐:试用在 2 个机器小时或 7 天后结束。Koyeb 包含一个免费实例,但它的注册和计费流程在部署前需要留意。Hugging Face 保留了免费的 Static Spaces,而新建的 Gradio 和 Docker Space 除了有限的 ZeroGPU 例外之外都需要付费账户。Railway 已经不再属于这份警示名单:在含 5 美元额度的 30 天试用之后,它现在会转为 0 美元的 Free 套餐,每月 1 美元资源额度。
接着是数据问题,一个能正常运行的应用往往就是在这里悄无声息地坏掉的。Render 的免费 Web 服务运行在临时文件系统上,其文档直言不讳:写到那里的任何内容,明确包括上传的图片和本地 SQLite 数据库,都会在每次重新部署、重启和休眠时丢失。Vercel 的函数运行在只读文件系统上,只有一个临时的暂存空间,所以你的应用写入的 SQLite 文件在那里同样不安全。如果你的应用把状态保存在文件里,就把它迁移到持久存储:托管数据库、对象存储,或者平台支持的持久卷。
在做决定之前,值得亲身感受一下休眠。在 Render 的免费套餐上,闲置 15 分钟服务就会进入休眠,下一次请求要 大约一分钟才能唤醒它,期间会显示一个加载页面 给点了你链接的人看。对作品集项目来说,耸耸肩就过去了。对一个在通话中打开链接的客户来说,那是糟糕的六十秒。
PythonAnywhere 的坑更隐蔽一些。它没有闲置休眠,但免费 Web 应用有一个月的有效期,除非你点击 PythonAnywhere 通过邮件发来的续期链接,否则会停止;此外免费账户的 出站互联网访问受限 ,只能访问白名单内的外部主机。一个调用白名单外 API 的应用会好端端地保持在线,却对那个未获允许的 API 的每次请求都失败(调试起来相当折磨,因为哪里看起来都没坏)。
作为静态站点发布
如果你的应用所做的一切都不需要在请求时运行服务器代码,那么静态托管就是这里最接近「配置一次就不用管」的选项:它可以在没有你笔记本的情况下保持在线,也没有等待被唤醒的应用进程。托管方的账户和用量限制仍然适用。符合条件的应用比你想象的多,这也是为什么在假定自己需要服务端托管之前,值得先回答那三个问题中的第一个。
这条规则比「它有没有后端」更窄。只要你的应用发出的每个请求,要么指向你自己的静态文件,要么直接从浏览器发往别人的 API,它就符合条件。从浏览器请求 Supabase 或公开 API 没有问题,只有一点要注意:放在浏览器代码里的密钥就是你已经公开的密钥。真正关上这扇门的,是需要为每个请求在服务器上运行你自己的代码。
如果符合条件,流程很短:
npm run build # Vite writes to dist/, a Next.js static export writes to out/
然后把构建产物部署到静态托管上。Cloudflare Pages、Netlify 和 Vercel 都可以从关联的仓库直接构建。GitHub Pages 可以从某个分支发布静态文件,或者用 GitHub Actions 运行你框架的构建并部署生成的产物。
GitHub Pages 的限制足够明确,值得一开始就考虑。仅限静态,每个账户只能有一个用户或组织站点,并且其使用限制明确指出它 不是给企业用的免费托管,也不适用于电商站点或商业 SaaS。如果你的应用要收款,那在开始之前就被排除了。
这条路径没有应用进程过期的问题。只要托管账户保持活跃且在限额之内,它就会一直工作;一旦应用在请求时需要服务端处理,它就不再合适了。
免费路径的尽头在哪里
免费路径不是同时失效,而是各自在不同的地方走到头。静态托管已经解决了笔记本在线时长的问题,还给了你一个稳定的服务商 URL。免费应用托管也能做到这一点,其中一些现在还包含持久存储。当各种需求叠加起来时,VPS 才开始有意义:你自己的服务端进程需要一直在线,你需要可预期的持久存储,而免费套餐的资源或使用规则已经不再合适。
这道门槛值得尊重。一次演示不是买服务器的理由,一个低流量的静态项目也不是。短期分享就继续用隧道,应用真正是静态的就继续用静态托管,限额还能容纳你所运行的内容就继续用免费应用托管。
当你需要一台由自己掌控、始终在线的服务器,并且愿意承担随之而来的运维工作时,再迁移到 VPS。 我们的 Linux VPS 是一个选择,其他服务商的同类 VPS 也能完成同样的工作。按应用需求来选服务器规格,而不是假设最小的套餐就够用。
在拥有 root 权限、NVMe 与 AMD EPYC 强大性能的 Linux VPS 上构建应用。
查看 Linux 套餐这么做之后会有两处变化。平台托管给你的「推送到 git 即部署」的工作流不再是自动的了。你可以用 Coolify 或 Dokku这样的自托管 PaaS 把它重建出来,或者搭建自己的 CI/CD 流程,但无论哪种方式,更新和维护现在都归你负责。
另一处变化是,这台机器上能放的不只是应用。如果你也想把编辑器搬到那里,它会很乐意 运行 Code Server 和 Claude Code ,这究竟是个不错的附加福利,还是一个全新的周末工程,就看你自己了。
常见问题
为什么我合上笔记本后公开 URL 就失效了?
因为隧道绑定的是创建它的那个进程,而不是你的应用。合上笔记本或关掉终端会结束那个进程,公开 URL 随之失效,而你的应用本身没有任何问题。重启隧道会得到一个新 URL,除非工具给你分配了保留域名。如果链接需要在笔记本睡眠时依然可用,应用就必须离开你的机器。
把本地应用放到公开 URL 上需要域名吗?
几乎所有情况下都不需要。Cloudflare 快速隧道、ngrok 分配的开发域名、localtunnel、localhost.run 以及上面的免费托管套餐,都会免费给你分配一个它们自己域名下的子域名。例外是 Cloudflare 的命名隧道,它需要一个你已经添加到 Cloudflare DNS 的域名。
其他 Wi-Fi 网络上的人能打开我的本地 IP 地址吗?
不能。像 192.168.1.42 这样的地址,指向的是你当前所在网络中恰好持有它的那台设备,而在任何其他网络里,它指向的是另一台设备,或者什么都不是。你 Wi-Fi 之外的任何人都需要改用隧道、免费托管套餐或静态托管。
如果我的应用有登录和数据库,哪些方法可行?
同一网络和隧道这两条路径无需改动就能用,因为应用仍然运行在你的机器上。如果登录和数据库请求直接从浏览器发往托管服务,并且没有任何请求需要运行私有的服务端代码,静态部署也可行。如果你自己的认证或数据库代码在请求时需要服务器,就改用服务端托管。在免费应用托管上,请把持久数据放在持久存储中,而不是假设应用的本地文件系统能保留下来。

讨论
评论
登录后参与讨论。