Перейти до основного вмісту
Знижка 50% усі плани, обмежений час. Від $2.48/mo
17 min left
Інструменти розробника та DevOps

Як опублікувати localhost-застосунок без VPS

B Автор: Bill 17 хв читання
Заставка «Публікуємо localhost без VPS»: локальний застосунок на ноутбуці розходиться через тунель до телефона, вікна браузера та домашньої мережі

Ваш застосунок працює. Ви запустили dev-сервер, відкрили http://localhost:3000, і він робить те, що має. Потім хтось просить посилання, і з'ясовується, що URL на вашому екрані нічого не означає ні для кого, крім вас.

Є три способи дати localhost-застосунку публічний URL без VPS, плюс один швидший варіант, коли співрозмовник у вашій локальній мережі, і вибір між ними — це не питання інструмента. Це питання того, як довго застосунок має залишатися доступним і чи може він працювати десь, окрім вашого ноутбука. Нижче — кожен шлях, команда, яка дає URL, і саме те, що змусить цей URL перестати працювати.

TL;DR

  • Комусь у вашому Wi-Fi треба це побачити: прив'яжіть dev-сервер до всіх мережевих інтерфейсів і повідомте свою LAN-IP. Готово за секунди, помирає тієї миті, коли гість залишає вашу мережу.
  • Потрібне посилання, яке будь-хто відкриє протягом наступної години: запустіть тунель (cloudflared, ngrok, localtunnel, localhost.run). Публічний HTTPS-URL приблизно за хвилину, жодних змін на роутері, і він помирає разом із процесом, який його запустив.
  • Він має працювати, поки ноутбук закритий: відправте застосунок на безкоштовний тариф хостингу. Це відв'язує застосунок від ноутбука і вводить нові правила: банківські картки, комерційне використання і те, чи переживуть ваші дані перезапуск.
  • Вашому застосунку не потрібен серверний код у момент запиту: зберіть його і викладіть статичний результат на статичний хостинг. Він може залишатися онлайн без вашого ноутбука, і там немає процесу застосунку, який треба будити, поки обліковий запис і ліміти хостингу це дозволяють.
  • Одне значення за замовчуванням, про яке варто знати: next dev та python -m http.server уже слухають усі мережеві інтерфейси без жодних прапорців. Якщо ви вважали, що ваш dev-сервер видно лише на вашому ноутбуці, це не так.

Який шлях підходить вашому застосунку

Схема ухвалення рішення від локального застосунку до одного з чотирьох шляхів: лише LAN, публічний тунель, хостинг застосунків або статична збірка, з часом налаштування, можливістю вимкнути ноутбук і найкращим сценарієм для кожного

Три з цих чотирьох шляхів дають застосунку публічний URL в інтернеті; перший сягає лише вашої мережі, що робить його водночас найшвидшим і найобмеженішим. Відсортуйте їх за тим, як довго має жити URL, і вибір майже зробить себе сам.

ШляхЧас до отримання URLСкільки живеЩо його вбиваєДля кого
Одна мережаСекундиПоки ви обоє в мережіГість переходить в інший Wi-FiКолега за сусіднім столом або ваш власний телефон
ТунельБлизько хвилиниПоки працює процесЗакрити ноутбук, убити термінал, впертися в ліміти тарифуДемо, показ клієнту, тест вебхука
Безкоштовний тариф хостингуВід 10 до 30 хвилинБезстроково, з умовамиПрисипляння, ефемерна файлова система або умови тарифуТе, що має відповідати, поки ви спите
Статична збіркаВід 10 до 20 хвилинБезстроковоПотреба в серверному коді в момент запитуЗастосунки, які можна повністю згенерувати під час збірки або виконувати на клієнті

Які рядки вам доступні, залежить від трьох речей, які можна перевірити у власному проєкті:

  • Чи потрібен застосунку ваш серверний код у момент запиту? Маршрут Flask або FastAPI, ендпоінт у server.js або будь-яка серверна логіка, залежна від запиту, потребує серверного хостингу. Серверний код на етапі збірки не виключає статичне розгортання автоматично: Server Components у Next.js можуть виконуватися під час next build, а статичні GET Route Handlers можна пререндерити. Якщо кожен запит під час роботи можна віддати як статичні файли або надіслати напряму з браузера в зовнішній API, статичний шлях досі відкритий.
  • Чи читає або пише він файл, який треба зберігати? Файл бази даних (.db, .sqlite), тека завантажень, JSON-файл, який він редагує. Якщо так, перевірте модель зберігання хостингу перед розгортанням. Безкоштовні вебсервіси Render і безкоштовні інстанси Koyeb використовують ефемерне локальне сховище, а Vercel Functions працюють із файловою системою лише для читання і тимчасовим простором /tmp . Тримайте постійний стан у надійному томі, базі даних або об'єктному сховищі, а не розраховуйте, що локальний диск застосунку вціліє.
  • Чи потрібен йому секрет під час роботи? Ключ у файлі .env працює без змін на перших двох шляхах, бо застосунок досі запущений на вашій машині. На двох інших ви вводите його заново в налаштуваннях середовища провайдера, і його не має бути в репозиторії, який ви пушите.

Поділіться у своїй мережі

next dev уже слухає всі мережеві інтерфейси вашої машини (саме це й означає 0.0.0.0 , коли ви його бачите), і те саме робить python -m http.server. Жодному з них не потрібен прапорець, тож dev-сервер, який у вас запущений просто зараз, найімовірніше, вже доступний із телефона в тому ж Wi-Fi.

Next.js описує -H як спосіб змінити це ім'я хоста, зі значенням за замовчуванням 0.0.0.0, а документація Python каже, що модуль прив'язується до всіх інтерфейсів , якщо не передати --bind 127.0.0.1. Це робить такий спосіб найшвидшим, щоб поділитися localhost-застосунком: без облікового запису, без встановлення, без розгортання. Іншим популярним dev-серверам треба сказати про це явно.

# 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, оскільки саме він його запускає. 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 ГБ трафіку, 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.

Що б ви не обрали, чітко розумійте, що саме ви роздали. З публічним тунелем без контролю доступу все, що віддає dev-сервер, доступне будь-кому, хто має цей URL, включно з маршрутами, на які ви ніколи не посилалися, і будь-яким налагоджувальним інтерфейсом, який ви залишили ввімкненим. Нормально для п'ятнадцятихвилинного демо. Значно гірше для URL, вставленого в публічний Discord.

Закінчення терміну застає зненацька, бо виглядає так, ніби застосунок зламався. Більшість швидких шляхів тут досі залежать від тунельного ПЗ на вашому ноутбуці: зупиніть cloudflared, ngrok, localtunnel або SSH-сесію localhost.run — і прокидання припиниться. Tailscale Funnel — виняток, якщо запустити його з --bg: тоді конфігурація Funnel далі працює у фоні та відновлюється після перезавантаження. Жоден із них не зможе віддавати ваш локальний застосунок, поки сам ноутбук офлайн. Тунель можна тримати запущеним днями, і він працюватиме рівно доти, доки не закриється кришка або ви не впретеся в ліміт запитів.

Тунель — правильний інструмент для демо і неправильний для хостингу: його аптайм дорівнює аптайму вашого ноутбука.

Нехай застосунок покине вашу машину

Це перший шлях, на якому ваш ноутбук перестає бути опорою, і перший, де умови важливіші за інструменти. Закінчує все тут не таймер. Це присипляння, скидання файлової системи або тариф, який вирішив, що ваш застосунок — не те, що він хоче бачити на безкоштовному інстансі.

Варіанти нижче — від постійних безкоштовних тарифів до коротких пробних періодів. Одні можуть тримати застосунок онлайн безстроково в межах своїх лімітів; інші зупиняються після фіксованого пробного періоду або вимагають платний обліковий запис за обчислення. Перевірте правила оплати, сну і зберігання перед розгортанням.

Умови безкоштовних тарифів нижче звірено зі сторінками цін або документації кожного провайдера 7 вересня 2026 року.

ПоставщикКомерційне використання?Чи переживають дані перезапуск?Підступ
NetlifyДозволеноТак, з Netlify Blobs або DatabaseКартка для старту не потрібна; якщо витратити всі місячні кредити тарифу Free, проєкти ставляться на паузу до наступного розрахункового періоду, якщо не перейти на платний тариф
RenderНе зазначеноВтрачаються при перезапускуКартка для старту не потрібна; засинає після 15 хвилин простою; безкоштовний Postgres спливає через 30 днів після створення
Cloudflare Pages / WorkersНе зазначеноТак, з KV, D1, R2 або Durable Objects500 збірок Pages на місяць; безкоштовний Workers обмежений 100 000 запитами на день
VercelНе на HobbyВтрачаються при перезапускуHobby лише для особистого використання; файлова система функцій доступна лише для читання
GitHub PagesЗабороненоЛише статичний результатЖодного онлайн-бізнесу, e-commerce чи комерційного SaaS; таймаут розгортання 10 хвилин
PythonAnywhereНе зазначеноТакБезкоштовні облікові записи сягають лише білого списку зовнішніх хостів; безкоштовний вебзастосунок спливає через місяць , якщо його не продовжити
Fly.ioНе зазначеноТак, з Fly VolumeПостійного безкоштовного тарифу немає: 2 машино-години або 7 днів; пробні Machines автоматично зупиняються через 5 хвилин роботи, а пробний період включає 20 ГБ сховища на томах; після завершення пробного періоду застосунки зупиняються, доки ви не додасте картку
KoyebНе зазначеноНемає постійного локального сховищаПотрібна картка; Koyeb робить і скасовує предавторизацію на 29 $, але під час реєстрації за замовчуванням обрано Pro, і його пропорційна вартість списується, якщо не перемкнутися на Starter
Hugging Face SpacesНе зазначеноВтрачаються при перезапускуStatic Spaces безкоштовні; Gradio і Docker загалом вимагають платного тарифу, але відповідні безкоштовні особисті облікові записи можуть розміщувати до двох Gradio Spaces на ZeroGPU
RailwayНе зазначеноТак, якщо застосунок використовує включений том на 0,5 ГБ30-денний пробний період із разовим кредитом 5 $, потім тариф Free за 0 $ з 1 $ кредиту на ресурси на місяць; картка не потрібна

«Не вказано» означає, що власні сторінки провайдера не відповідають на це питання для безкоштовного тарифу. Вважайте це невідомим, а не «так» чи «ні».

Три з цих рядків заслуговують на окремий погляд перед стартом. У Fly.io немає постійного безкоштовного тарифу: пробний період закінчується через 2 машино-години або 7 днів. У Koyeb є безкоштовний інстанс, але процес реєстрації та оплати потребує уваги перед розгортанням. Hugging Face залишає Static Spaces безкоштовними, тоді як нові Gradio- і Docker-Spaces вимагають платного облікового запису, за винятком обмеженого винятку ZeroGPU. Railway більше не місце в цьому списку попереджень: після 30-денного пробного періоду з 5 $ кредиту він тепер переходить на тариф Free за 0 $ з 1 $ кредиту на ресурси на місяць.

Далі постає питання даних — саме тут робочий застосунок непомітно перетворюється на зламаний. Безкоштовні вебсервіси Render працюють на ефемерній файловій системі, і документація каже прямо: усе, що туди записано, включно із завантаженими зображеннями та локальними базами SQLite, втрачається при кожному повторному розгортанні, перезапуску та присиплянні. Функції Vercel працюють на файловій системі лише для читання з одним лише тимчасовим робочим простором, тож файл SQLite, який пише ваш застосунок, там теж не в безпеці. Якщо застосунок зберігає стан у файлі, перенесіть його в надійне сховище: керовану базу даних, об'єктне сховище або постійний том там, де платформа його пропонує.

Присипляння варто відчути до того, як ви на щось підпишетеся. На безкоштовному тарифі Render 15 хвилин простою відправляють сервіс спати, а наступний запит будить його приблизно за хвилину, показуючи сторінку завантаження тому, хто клікнув по вашому посиланню. Для портфоліо — знизати плечима. Для клієнта, який відкриває посилання під час дзвінка, — шістдесят дуже поганих секунд.

У PythonAnywhere пастка тонша. Присипляння через простій немає, але безкоштовний вебзастосунок спливає через місяць і зупиняється, якщо не клікнути по посиланню продовження, яке 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 обмеження досить жорсткі, щоб враховувати їх від самого початку. Лише статика, один користувацький або організаційний сайт на обліковий запис, і умови використання, де сказано, що це не безкоштовний хостинг для бізнесу, e-commerce-сайту чи комерційного SaaS. Якщо застосунок прийматиме платежі, це виключає його ще до початку.

У цього шляху немає закінчення процесу застосунку. Він далі працює, поки обліковий запис хостингу активний і в межах лімітів, і перестає підходити, щойно застосунку потрібна серверна робота в момент запиту.

Де закінчуються безкоштовні шляхи

Безкоштовні шляхи ламаються в різних місцях, а не всі одразу. Статичний хостинг уже вирішує питання аптайму ноутбука і дає стабільний URL провайдера. Безкоштовний хостинг застосунків може те саме, а деякі тепер включають постійне сховище. VPS починає мати сенс, коли вимоги накопичуються: ваш власний серверний процес має залишатися онлайн, потрібне передбачуване постійне сховище, а правила ресурсів чи використання безкоштовного тарифу більше не підходять.

Цей поріг варто поважати. Одне демо — не привід купувати сервер, як і статичний проєкт із низьким трафіком. Залишайтеся на тунелі для короткого шерингу, на статичному хостингу — поки застосунок справді статичний, і на безкоштовному хостингу застосунків — поки його ліміти відповідають тому, що ви запускаєте.

Переходьте на VPS, коли вам потрібен постійно працюючий сервер під вашим контролем і ви готові взяти на себе супутню експлуатаційну роботу. Наш Linux VPS — один із варіантів, і порівнянний VPS в іншого провайдера впорається з тим самим завданням. Підбирайте сервер під застосунок, а не розраховуйте, що вистачить найменшого тарифу.

Переглянути тарифи Linux

Розробляйте на Linux VPS з root-доступом, NVMe та потужністю AMD EPYC.

Переглянути тарифи Linux

Після цього змінюються дві речі. Робочий процес розгортання «push у git», який давав платформний хостинг, більше не автоматичний. Його можна відтворити за допомогою self-hosted PaaS на кшталт Coolify або Dokkuабо побудувати власний 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 потрібен тунель, безкоштовний тариф хостингу або статичний хостинг.

Що з цього працює, якщо в застосунку є вхід і база даних?

Шляхи «одна мережа» і «тунель» працюють без змін, бо застосунок досі запущений на вашій машині. Статичне розгортання теж може працювати, якщо запити входу й бази даних ідуть напряму з браузера до розміщеного сервісу і для жодного запиту не треба виконувати приватний серверний код. Якщо вашому власному коду автентифікації або бази даних потрібен сервер у момент запиту, використовуйте серверний хостинг. На безкоштовному хостингу застосунків тримайте постійні дані в надійному сховищі, а не розраховуйте, що локальна файлова система застосунку вціліє.

Поділитися

Обговорення

Коментарі

Увійдіть, щоб долучитися до обговорення.

Більше з блогу

Продовжуйте читати.

Готові розгортати? Від $2,48/міс.

Незалежна хмара з 2008 року. AMD EPYC, NVMe, 40 Gbps. Повернення коштів за 14 днів.