
Как сделать постоянные URL-адреса для локальных Localhost-проектов
Чтобы локальный адрес не менялся вместе с портом, закрепите имя проекта и поставьте перед dev-сервером обратный прокси-сервер (reverse proxy) на порты 80 и 443.

Чтобы локальный адрес не менялся вместе с портом, закрепите имя проекта и поставьте перед dev-сервером обратный прокси-сервер (reverse proxy) на порты 80 и 443. Для одного компьютера удобно имя вида app.localhost: оно ведёт на loopback, петлевой адрес самого устройства, без правки DNS. Для телефона и других устройств в локальной сети (LAN) нужен другой контур — стабильный LAN-IP компьютера, локальный DNS, например app.devbox.test, reverse proxy и правило firewall. Временный tunnel создаёт уже публичную ссылку и не заменяет локальный hostname.
Что именно должно оставаться постоянным
URL https://app.devbox.test/account складывается из схемы, имени, порта и пути. Если порт не указан, браузер использует 443 для HTTPS или 80 для HTTP. Файл hosts и DNS способны связать имя с IP-адресом, но не с :5173 или :3000. Порт скрывает reverse proxy: он принимает запрос на 80/443, читает имя хоста и передаёт трафик нужному приложению на его внутренний порт.
| Задача | Практичный адрес | Что требуется | Главная граница |
|---|---|---|---|
| Один проект на одном ПК, порт допустим | http://shop.localhost:5173 |
Dev-сервер с закреплённым портом | С телефона адрес откроет loopback телефона |
| Несколько проектов на одном ПК, без портов | https://shop.localhost, https://api.localhost |
Caddy, nginx или Traefik на 80/443 | Proxy должен быть запущен |
| Реалистичные поддомены и HTTPS | https://app.devbox.test |
hosts или локальный DNS, reverse proxy, локальный сертификат |
Корневому центру сертификации должен доверять каждый клиент |
| Телефон, планшет, второй компьютер в LAN | https://app.devbox.test |
Стабильный LAN-IP, внутренний DNS, proxy, firewall | .localhost для этого не подходит |
| Несколько контейнеров | Тот же внешний URL, внутри app:5173 |
Общая Docker-сеть и proxy | IP контейнера нельзя считать постоянным |
| OAuth, webhook или показ человеку из другой сети | https://preview.example.com |
Защищённый named tunnel или собственный домен | Это публичная точка входа, а не локальное имя |
Практичная лестница решений выглядит так: сначала *.localhost, затем локальный reverse proxy, при необходимости .test и локальный HTTPS, после этого LAN DNS. Публичный tunnel добавляют только для задачи, которой действительно нужен вход из интернета.
Как выбрать имя без будущего конфликта
Локальное имя нужно выбирать по области действия. Похожая запись в адресной строке ещё не означает одинаковое разрешение имён.
| Суффикс | Где уместен | Как разрешается | Чего не делать |
|---|---|---|---|
.localhost |
Один компьютер | Любое имя внутри зоны должно вести на loopback IPv4/IPv6 | Не использовать для доступа к другому компьютеру |
.test |
Тестовая зона в hosts или локальном DNS |
Как обычное DNS-имя, но зарезервировано для тестов | Не ожидать, что оно заработает без записи |
| Поддомен принадлежащего вам домена | Команда, split DNS, постоянный tunnel | По вашим публичным или внутренним DNS-правилам | Не смешивать публичный и внутренний ответ без плана |
.local |
Обнаружение устройств через mDNS/Bonjour | Multicast в текущем сетевом сегменте | Не строить на нём обычную wildcard DNS-зону команды |
Придуманный .dev, .app или другой TLD |
Не рекомендуется | Может оказаться реальным публичным доменом | Не полагаться на то, что имя «никто не зарегистрирует» |
Специальное поведение .localhost и тестовое назначение .test закреплены в RFC 6761. Это важнее привычки использовать project.local: .local обслуживает Multicast DNS, может конфликтовать с Bonjour, по-разному работать между VLAN и получать числовой суффикс при совпадении имён.
Для одного ПК начните с shop.localhost. Для общей сети выберите shop.devbox.test либо поддомен реального домена, которым владеет команда. Не подменяйте локальную настройку публичной A-записью на 127.0.0.1 или частный адрес 192.168.x.x: это смешивает два контура и может попасть под защиту от DNS rebinding.
Самый простой рецепт на одном компьютере
Если порт в адресе не мешает, файл hosts не нужен. Закрепите порт dev-сервера и обращайтесь к проекту через поддомен .localhost:
http://shop.localhost:5173
Для Vite порт лучше сделать строгим. Иначе при занятом 5173 сервер без ошибки перейдёт на 5174, и URL снова изменится:
npm run dev -- --host 127.0.0.1 --port 5173 --strictPort
Проверьте имя и порт в PowerShell:
[System.Net.Dns]::GetHostAddresses('shop.localhost')
Test-NetConnection shop.localhost -Port 5173
curl.exe -I http://shop.localhost:5173
Сервер, доступный только через 127.0.0.1, не принимает соединения с LAN. Это безопасная настройка по умолчанию. Если приложение должно открываться с телефона, не меняйте bind на 0.0.0.0 автоматически — сначала настройте LAN-сценарий и ограничьте firewall.
Когда всё-таки нужен файл hosts
hosts пригодится для точного имени в зоне .test, например app.devbox.test. На Windows файл находится в %SystemRoot%\System32\drivers\etc\hosts, а редактору нужны права администратора.
Сначала создайте резервную копию в пользовательской временной папке и откройте системный файл:
$hostsPath = Join-Path $env:SystemRoot 'System32\drivers\etc\hosts'
$backupPath = Join-Path $env:TEMP ("hosts-{0}.bak" -f (Get-Date -Format 'yyyyMMdd-HHmmss'))
Copy-Item -LiteralPath $hostsPath -Destination $backupPath
Start-Process notepad.exe -ArgumentList $hostsPath -Verb RunAs
Добавьте только нужные имена. Несколько алиасов можно указать в одной строке:
127.0.0.1 app.devbox.test api.devbox.test
После сохранения очистите системный кэш и проверьте результат:
Clear-DnsClientCache
[System.Net.Dns]::GetHostAddresses('app.devbox.test')
curl.exe -I http://app.devbox.test:5173
Эта запись не убирает :5173. Она также действует только на данном компьютере и не поддерживает *.devbox.test. Для маски всех поддоменов (wildcard) нужен локальный DNS, а для адреса без порта — reverse proxy.
При откате удалите ровно добавленную строку и снова выполните Clear-DnsClientCache. Не копируйте старый backup поверх текущего файла, пока не сравните версии: после резервного копирования другой инструмент или пользователь мог добавить свои записи.
Пути и очистка кэша в macOS и Linux
| Система | Файл | Типичная очистка кэша | Оговорка |
|---|---|---|---|
| Windows 10/11 | %SystemRoot%\System32\drivers\etc\hosts |
Clear-DnsClientCache |
Браузерный Secure DNS может иметь отдельный кэш |
| macOS | /etc/hosts |
sudo dscacheutil -flushcache, затем sudo killall -HUP mDNSResponder |
Не используйте .local как обычную DNS-зону |
| Linux с systemd-resolved | /etc/hosts |
sudo resolvectl flush-caches |
Resolver и команда зависят от дистрибутива |
В macOS и Linux сначала копируют /etc/hosts в отдельный backup, затем редактируют через sudo. Принцип отката тот же: удалить собственную строку, не затирая чужие изменения.
Как убрать порты с помощью Caddy
Reverse proxy слушает стандартные 80/443 и направляет запрос по hostname. Для локальной разработки Caddy удобен тем, что конфигурация короткая, WebSocket проксируется без отдельной секции, а HTTPS можно выпустить через встроенный локальный центр сертификации.
| Proxy | Когда выбирать | Сильная сторона | Что потребует внимания |
|---|---|---|---|
| Caddy | Один разработчик, небольшой набор проектов | Короткий файл и локальный HTTPS | Доверие к локальному CA и постоянное хранилище /data |
| nginx | Команда уже использует nginx в production | Полный явный контроль | Сертификаты, proxy headers и WebSocket настраиваются вручную |
| Traefik | Много динамических Docker-сервисов | Маршруты через labels и автоматическое обнаружение | Защита Docker socket и exposedByDefault=false |
Создайте рядом с проектом файл Caddyfile:
app.localhost {
tls internal
reverse_proxy 127.0.0.1:5173
}
api.localhost {
tls internal
reverse_proxy 127.0.0.1:8080
}
До запуска убедитесь, что нужные порты свободны:
Get-NetTCPConnection -State Listen |
Where-Object LocalPort -In 80, 443, 5173, 8080 |
Select-Object LocalAddress, LocalPort, OwningProcess
Проверьте конфигурацию, затем запустите proxy:
caddy validate --config .\Caddyfile
caddy run --config .\Caddyfile
После этого внешние адреса не содержат порт:
https://app.localhost/
https://api.localhost/
Caddy попробует установить собственный корневой сертификат в trust store. Если процессу не хватило прав, браузер покажет ошибку доверия. На личной машине разработчика можно проверить созданный CA и выполнить caddy trust из повышенной консоли. Не устанавливайте неизвестный корневой сертификат и не передавайте его private key.
Проверка без -k важна: curl -k отключает валидацию TLS и доказывает только доступность сервера.
curl.exe -I https://app.localhost/
Если приложение строит ссылки как http://127.0.0.1:5173, настройте доверие к proxy в самом framework и внешний APP_URL. Caddy передаёт стандартные X-Forwarded-* заголовки, но приложение должно обрабатывать их только от доверенного proxy.
Кроссплатформенный вариант Docker Compose и Caddy
Следующий пример рассчитан на Vite-приложение, которое собирается из текущей папки. Для другого стека замените build, command и внутренний порт, сохранив принцип: приложение не публикуется на хост, а доступно только proxy в общей сети.
name: localdev-shop
services:
app:
build: .
command: ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173", "--strictPort"]
expose:
- "5173"
networks:
- devnet
proxy:
image: caddy:2-alpine
depends_on:
- app
ports:
- "127.0.0.1:80:80"
- "127.0.0.1:443:443"
- "[::1]:80:80"
- "[::1]:443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- devnet
networks:
devnet:
volumes:
caddy_data:
caddy_config:
В контейнерном Caddyfile upstream задаётся именем сервиса, а не localhost и не IP контейнера:
app.localhost {
tls internal
reverse_proxy app:5173
}
Запуск и базовая проверка:
docker compose config
docker compose up -d --build
docker compose ps
docker compose logs proxy --tail 100
curl.exe -kI https://app.localhost/
Последняя команда использует -k только для первичной диагностики маршрута. Для полноценной проверки нужно доверить корень Caddy на хосте и повторить запрос без -k. Контейнер не может автоматически установить CA в trust store Windows, macOS или Linux. Скопируйте публичный корневой сертификат после первого запуска:
docker compose cp proxy:/data/caddy/pki/authorities/local/root.crt .\.local-caddy-root.crt
На Windows импортируйте его в хранилище доверия текущего пользователя и сохраните точный цифровой отпечаток (thumbprint) для отката:
$importedCert = Import-Certificate `
-FilePath .\.local-caddy-root.crt `
-CertStoreLocation Cert:\CurrentUser\Root
$importedCert.Thumbprint | Set-Content .\.local-caddy-root.thumbprint
curl.exe -I https://app.localhost/
На macOS сертификат добавляют в Keychain Access или командой security add-trusted-cert; на Debian/Ubuntu — копируют с расширением .crt в /usr/local/share/ca-certificates/ и запускают update-ca-certificates. Firefox, Node.js, Java, контейнеры и мобильные устройства могут использовать отдельные trust stores. Проверяйте каждый реальный клиент, а не только Chrome на компьютере.
Named volume caddy_data сохраняет локальный CA между перезапусками. Обычный docker compose down не удаляет его. Команда docker compose down -v создаст при следующем запуске новый CA и сломает прежнее доверие, поэтому -v нельзя добавлять в автоматическую очистку.
Где нужен mkcert
mkcert удобен, если TLS завершает nginx, встроенный Node-сервер или другой инструмент без собственного локального центра сертификации (CA). Утилита создаёт локальный CA, устанавливает его корень в поддерживаемые хранилища доверия и выпускает сертификаты с нужными альтернативными именами (Subject Alternative Names, SAN).
mkcert -install
mkcert `
-cert-file .\certs\devbox.test.pem `
-key-file .\certs\devbox.test-key.pem `
"devbox.test" "*.devbox.test"
Wildcard *.devbox.test не покрывает само имя devbox.test, поэтому в команде указаны оба имени. Он также не покрывает два уровня вроде x.api.devbox.test. Сертификат и private key затем явно подключают в Caddy/nginx/приложении: mkcert не меняет конфигурацию сервера.
Добавьте certs/*.pem, локальные CA-файлы и thumbprint-файлы в .gitignore. Особо защищайте файл rootCA-key.pem в каталоге, который показывает mkcert -CAROOT: тот, кто получит его, сможет выпускать сертификаты, доверенные вашей машиной. Для команды безопаснее версионировать Compose/Caddyfile и давать каждому разработчику создать собственный CA, чем раздавать один private key.
Постоянный адрес для телефона и других устройств в LAN
app.localhost на телефоне означает «этот телефон». Чтобы открыть приложение на ноутбуке, нужны пять согласованных частей:
- Закрепите за dev-компьютером LAN-IP через DHCP reservation на роутере, например
192.168.1.50. Ручной статический IP вне DHCP-пула тоже возможен, но им сложнее управлять. - Создайте внутреннее DNS-правило
app.devbox.test → 192.168.1.50или wildcard*.devbox.test → 192.168.1.50. - Привяжите proxy к LAN-IP на 80/443, а само приложение оставьте доступным только proxy.
- Разрешите вход на 80/443 только из нужной private subnet в firewall.
- Установите доверенный корень локального CA на каждое тестовое устройство либо используйте домен и сертификат, которым эти устройства уже доверяют.
Для dnsmasq wildcard-правило выглядит так:
address=/devbox.test/192.168.1.50
В AdGuard Home можно создать DNS rewrite *.devbox.test на тот же IP. В Pi-hole точные Local DNS records создаются через интерфейс; wildcard обычно добавляют штатным способом конфигурации используемого dnsmasq/FTL, а не десятками строк на клиентах. Не открывайте DNS-сервис в интернет и ограничьте интерфейсы локальной сетью.
Клиенты должны действительно использовать этот resolver. Private DNS, DNS-over-HTTPS в браузере, VPN или корпоративная политика способны обойти настройку роутера. Проверяйте конкретный DNS-сервер:
nslookup app.devbox.test 192.168.1.1
Test-NetConnection 192.168.1.50 -Port 443
curl.exe -I https://app.devbox.test/
Адрес DNS-сервера в примере замените на фактический. Если DNS отвечает правильно, а соединения нет, проверьте bind proxy и firewall. Если всё работает на обычном Wi‑Fi, но не на гостевом, вероятна client isolation. mDNS между VLAN также не проходит без отражателя, но для обычной DNS-зоны он и не должен быть нужен.
Для Compose замените loopback-привязки proxy на точный LAN-IP:
ports:
- "192.168.1.50:80:80"
- "192.168.1.50:443:443"
Не используйте 80:80 без адреса, если доступ со всех интерфейсов не был намеренным: Docker обычно публикует такой порт на 0.0.0.0 и [::]. Подробная модель описана в документации Docker о публикации портов.
Как настроить dev-сервер за proxy
Proxy не отменяет защиту framework. Приложение должно принимать только ожидаемые hostname/origin и слушать ровно тот интерфейс, с которого к нему приходит proxy.
Vite
При нативном Caddy и нативном Vite оставьте bind на 127.0.0.1. В Compose используйте 0.0.0.0, но не публикуйте порт приложения на хост. Разрешайте точные имена:
import { defineConfig } from 'vite'
export default defineConfig({
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
allowedHosts: ['app.localhost', 'app.devbox.test'],
},
})
Не ставьте allowedHosts: true и cors: true как универсальное исправление: это открывает dev-сервер для DNS-rebinding запросов и может раскрыть исходники. Если Hot Module Replacement не подключается через HTTPS-proxy, сначала проверьте WebSocket в DevTools. Только затем задавайте внешний wss, hostname и clientPort: 443 в server.hmr.
Next.js и другие frameworks
Next.js запускается на нужном внутреннем интерфейсе и порту:
npx next dev -H 0.0.0.0 -p 3000
Дополнительные dev-origin перечисляются в next.config.js:
module.exports = {
allowedDevOrigins: ['app.devbox.test', '*.devbox.test'],
}
Сходные проверки есть у других стеков: Django ALLOWED_HOSTS и CSRF trusted origins, Rails config.hosts, Laravel APP_URL и trusted proxies. Добавляйте конкретные имена, а proxy headers принимайте только от своего proxy. Иначе злоумышленник в LAN или через DNS rebinding сможет подставить Host, а приложение построит опасную ссылку сброса пароля или OAuth callback.
Docker: три разных значения слова localhost
В контейнерной схеме легко перепутать три адресных пространства:
localhostв браузере — компьютер, на котором открыт браузер;localhostвнутри контейнераproxy— сам контейнер proxy;app:5173в Compose-сети — сервисappи его container port.
Контейнеры получают новые IP после пересоздания, но Compose сохраняет service name. Поэтому reverse_proxy 172.20.0.4:5173 хрупок, а reverse_proxy app:5173 воспроизводим. Host port вроде 5173 вообще не нужен для связи proxy → app.
Если контейнеру всё же нужен сервис, запущенный на хосте, Docker Desktop предоставляет host.docker.internal. На Linux поведение зависит от установки; может потребоваться явный host-gateway. Для командного проекта надёжнее поместить proxy и приложение в общую Compose-сеть, чем строить основную схему на платформенном исключении.
Cookies, service workers и один origin
Смена адреса меняет поведение браузера сильнее, чем кажется:
- scheme, hostname и port образуют origin, поэтому
http://app.localhost:3000иhttp://app.localhost:5173— разные origins; - cookie не изолируются портом; атрибут
Domainне содержит порт; - cookie без
Domainостаётся host-only и не передаётся соседнему поддомену; SameSite=NoneтребуетSecure, а production-like проверку лучше вести через доверенный HTTPS;__Host-cookie требует HTTPS,Path=/и отсутствиеDomain;- service worker привязан к origin и scope. Старый worker может продолжить обслуживать старый hostname/порт после миграции.
Для разработки frontend и API проще использовать один внешний origin: https://app.devbox.test/ и путь /api, который proxy отправляет бэкенду. Это уменьшает объём CORS и cookie-настроек. Если production использует app.example.com и api.example.com, тестируйте раздельные поддомены намеренно: задайте CORS allowlist, CSRF origins, credentials, SameSite и cookie scope.
После смены URL откройте DevTools → Application, проверьте Cookies и Service Workers, отмените ненужные регистрации и очистите данные старого origin. В консоли полезны две проверки:
window.location.origin
window.isSecureContext
HTTP на localhost, loopback и обычно поддоменах .localhost браузеры считают потенциально доверенным локальным контекстом. Произвольное имя .test и LAN-IP по HTTP такого исключения не получают, поэтому для телефона, WebAuthn, PWA и честной проверки Secure-cookie используйте HTTPS.
OAuth redirect URI без сюрпризов
Для web OAuth зарегистрируйте полный callback, например:
https://app.devbox.test/auth/google/callback
Провайдер обычно сравнивает scheme, hostname, port, path и завершающий / буквально. http://localhost:3000/callback, http://127.0.0.1:3000/callback и https://app.localhost/callback — три разных URI. Некоторые провайдеры разрешают HTTP только для localhost/loopback и отклоняют .test, даже если сертификат доверен локально. Тогда используйте зарегистрированное loopback-исключение или постоянный named tunnel на принадлежащем вам домене.
Для desktop/native приложения правило иное: рекомендуемый callback слушает случайный порт на 127.0.0.1 или [::1], открывается только на время авторизации и использует Proof Key for Code Exchange (PKCE). Разрешение менять порт для loopback native callback нельзя переносить на обычное web-приложение.
Не стройте callback из непроверенного входного Host. Задайте внешний base URL в конфигурации приложения и доверьте forwarded headers только своему proxy.
Когда нужен tunnel
Tunnel нужен, если запрос приходит не из вашей LAN: внешний OAuth-провайдер требует публичный callback, платёжный сервис отправляет webhook, заказчик открывает демо из другой сети. Пример временного запуска:
cloudflared tunnel --url http://127.0.0.1:5173
Quick Tunnel выдаёт случайный *.trycloudflare.com и меняет его при новом запуске. Такой URL не постоянен, не имеет гарантии доступности и не подходит для долгоживущего OAuth redirect. Стабильный адрес даёт named tunnel с закреплённым hostname либо собственная публичная среда preview.
Tunnel превращает dev-сервер в публичный origin, даже если входящий порт роутера закрыт. Перед запуском:
- включите Access/OAuth/basic auth, если сервис не должен быть доступен всем;
- используйте тестовые данные и отдельные секреты;
- отключите debug toolbar, admin-панель, directory listing и опасные dev-endpoints;
- оставьте exact allowed hosts/origins;
- проверяйте подпись webhook и защищайтесь от повторной доставки;
- остановите процесс сразу после теста.
Не отправляйте tunnel token в репозиторий или лог. Быстрое шифрованное соединение до провайдера tunnel не заменяет авторизацию вашего приложения.
Проверка по слоям
Не начинайте отладку с браузера. Проверяйте каждый слой отдельно — так понятнее, где именно сломалась цепочка.
| Слой | Windows/кроссплатформенная проверка | Успешный признак |
|---|---|---|
| Имя | [System.Net.Dns]::GetHostAddresses('app.devbox.test') или nslookup для DNS |
Нужный loopback/LAN-IP, без неожиданного публичного адреса |
| Listen socket | Get-NetTCPConnection -State Listen |
Proxy на 80/443, приложение на ожидаемом внутреннем порту |
| Compose | docker compose ps |
Оба сервиса running, нет случайного host port у app |
| Маршрут proxy | curl.exe -kI https://app.devbox.test/ |
Не 502; -k только на этом диагностическом шаге |
| TLS trust | curl.exe -I https://app.devbox.test/ |
Нет ошибки неизвестного CA или несовпадения имени |
| DNS в обход resolver | curl.exe --resolve app.devbox.test:443:127.0.0.1 -I https://app.devbox.test/ |
Proxy и сертификат работают независимо от DNS |
| Browser origin | window.location.origin |
Точно ожидаемая схема, hostname и порт |
| Secure context | window.isSecureContext |
true там, где нужны защищённые Web API |
| Удалённое устройство | DNS lookup, затем HTTPS-запрос с него | Разрешение, сеть и trust работают именно на клиенте |
Проверяйте IPv4 и IPv6 отдельно. Имя может вернуть ::1, пока сервер слушает только 127.0.0.1, или наоборот. Временно отключать IPv6 ради маскировки ошибки не стоит: лучше согласовать записи и listen sockets.
Типовые неисправности
| Симптом | Вероятная причина | Что проверить |
|---|---|---|
| Имя не разрешается | Опечатка, DNS-кэш, Secure DNS/VPN, клиент использует другой resolver | hosts, A/AAAA, nslookup <name> <dns-ip>, браузерный DNS |
Connection refused |
На IP/порту никто не слушает | Процесс, bind 127.0.0.1 против LAN-IP, конфликт 80/443 |
502 Bad Gateway |
Proxy обращается не туда | В Docker использовать app:5173, не localhost:5173 и не IP контейнера |
| Браузер сообщает о недоверенном сертификате | Root CA не установлен на этом клиенте или Caddy потерял volume | Issuer, Subject Alternative Names, fingerprint root, наличие caddy_data |
| Сертификат доверен, но имя не совпадает | Сертификат не содержит hostname | Выпустить сертификат с точными SAN; wildcard не покрывает корень и два уровня |
| 403 или сообщение blocked host/origin | Защита dev-сервера | Точный allowedHosts, allowedDevOrigins, CSRF/CORS allowlist |
| Redirect loop или ссылка содержит внутренний порт | Неверный внешний base URL/proxy trust | X-Forwarded-Proto, Host, trusted proxies, APP_URL |
| Страница есть, HMR не подключается | WebSocket идёт на внутренний порт/неверную схему | Network → WS, внешний wss, host и client port 443 |
| На ноутбуке работает, на телефоне нет | .localhost, loopback bind, firewall, guest isolation или нет LAN DNS |
LAN-IP, resolver телефона, bind proxy, private network profile |
| После смены URL показывается старое приложение | Service worker и Cache Storage старого origin | DevTools Application, unregister, Clear site data |
OAuth redirect_uri_mismatch |
Отличается хотя бы один компонент URI | Scheme, hostname, port, path, регистр, trailing slash в панели провайдера |
Как сделать настройку воспроизводимой для команды
Постоянный URL полезен только тогда, когда новый разработчик получает его без ручного квеста. В репозиторий стоит положить:
compose.yamlиCaddyfile;.env.exampleс внешними URL без секретов;- скрипт read-only проверки портов, DNS и HTTPS;
- короткую таблицу «имя → сервис → container port»;
- инструкции для Windows, macOS и Linux;
- точный rollback.
Не коммитьте hosts-файл целиком, private keys локального CA, tunnel credentials, OAuth client secrets и сертификаты с ключами. Для LAN документируйте владельца DNS-правила и DHCP reservation. Для каждого проекта назначьте уникальные имена и внутренние порты, даже если наружу все выходят через 443.
Очистка и откат
Откатывайте изменения в обратном порядке и только по точным идентификаторам:
- Остановите Quick/named tunnel и отзовите больше не нужный token.
- Удалите тестовый OAuth redirect из панели провайдера, если он больше не используется.
- Остановите proxy. Для Compose выполните
docker compose down; не добавляйте-v, пока CA нужен другим проектам. - Удалите только собственные
hosts-строки и очистите DNS-кэш. - Удалите точное wildcard/Local DNS rule и проверьте, что обычные запросы снова дают ожидаемый ответ.
- Удалите созданное firewall rule по его имени, не сбрасывая firewall целиком.
- Удалите локальный root CA только если ни один проект ему больше не доверяет. На Windows используйте сохранённый thumbprint:
$thumbprint = Get-Content .\.local-caddy-root.thumbprint
Remove-Item -LiteralPath "Cert:\CurrentUser\Root\$thumbprint"
- Удалите точные сертификаты и private keys проекта. Для mkcert команда
mkcert -uninstallубирает его CA из поддерживаемых trust stores текущей машины, но корень, вручную установленный на телефоне или другом компьютере, нужно удалить там отдельно. - Очистите cookie, Cache Storage и service workers старого origin.
Перед удалением CA сравните fingerprint и убедитесь, что он не общий для других локальных адресов. Простое удаление папки Caddy при оставшемся доверенном корне оставляет ненужное доверие, а удаление volume при оставшемся проекте вызывает неожиданную ротацию CA.
Вывод
Для одного компьютера оптимальная основа — *.localhost: имя зарезервировано, не требует hosts и не уходит в публичный DNS. Reverse proxy на 80/443 превращает app.localhost:5173 в постоянный https://app.localhost и раздаёт разные имена нескольким проектам. Для LAN используйте .test или поддомен собственного домена, стабильный IP dev-хоста, внутренний DNS, точную привязку proxy, firewall и доверенный HTTPS на каждом клиенте.
В Docker маршрутизируйте по service name, не по IP и не через localhost. В framework оставляйте точные allowed hosts/origins. OAuth, cookies и service workers проверяйте на окончательном origin. Tunnel считайте отдельной публичной публикацией: Quick URL временный, а постоянный внешний callback требует named tunnel или собственной preview-среды и обязательной защиты доступа. Читать полный обзор сервиса Docker
Автор статьи

Редактор и автор статей
Пишет экспертные материалы о цифровом маркетинге и автоматизации. Журналист с опытом в деловых медиа, отвечает за качество и достоверность публикаций.
Вопросы и ответы
Нет. hosts сопоставляет hostname с IP-адресом. Используйте порт в URL либо поставьте reverse proxy на 80/443, который направит имя на нужный внутренний порт.
Каждое устройство разрешает .localhost в собственный loopback. На телефоне это адрес телефона. Для доступа к ноутбуку нужен его LAN-IP, внутренний DNS и proxy, доступный из сети.
Для одного устройства — .localhost. Для управляемой тестовой DNS-зоны — .test. .local оставьте mDNS/Bonjour: обычная DNS-зона с таким суффиксом конфликтует с механизмами обнаружения устройств.
Нет. Перечисляйте точные имена либо настройте wildcard во внутреннем DNS, например dnsmasq или AdGuard Home. Поддомены .localhost обычно не требуют wildcard-записи, потому что вся зона указывает на loopback.
Не стоит. .dev — реальная публичная зона с принудительным HTTPS в браузерах. Используйте зарезервированный .test, .localhost или поддомен домена, которым вы владеете.
Не всегда: браузеры считают loopback потенциально доверенным для многих защищённых API. Но HTTPS нужен для production-like cookies, произвольного .test, LAN-устройств, части OAuth/WebAuthn-сценариев и проверки реального TLS-контура.
Trust store локален для устройства, а иногда и для приложения. Установите публичный корень своего local CA на телефон и включите доверие по правилам его ОС либо используйте публично доверенный сертификат на принадлежащем вам домене. Private key CA на телефон не копируют.
Внутри контейнера proxy localhost означает сам proxy-контейнер. Подключите оба сервиса к одной Compose-сети и направляйте запрос на app:5173, где app — имя сервиса.
Порт входит в origin, но не в область cookie. Два приложения на одном hostname и разных портах могут увидеть одни и те же подходящие cookies. Разводите проекты по hostname и используйте host-only cookies.
Нет. Быстрый tunnel выдаёт случайный hostname и меняет его при новом запуске. Для постоянного callback нужен named tunnel с закреплённым hostname или отдельная preview-среда.
Зависит от провайдера. Многие требуют публичный домен и точное совпадение URI, разрешая HTTP только для localhost/loopback. Сначала проверьте правила провайдера, затем зарегистрируйте точную схему, hostname, порт и путь.
Храните в Git Compose/Caddy-конфигурацию, список имён и скрипты проверки. Каждый разработчик создаёт и доверяет свой local CA. Общий CA допустим только при отдельном защищённом управлении ключом, ротации и процедуре отзыва.
Смотрите также

JavaScript, TypeScript, PHP, Python и Ruby: сравнение стеков
24 сентября 2026 г.

Ruby on Rails в 2026 году: когда выбирать фреймворк и жив ли он
20 сентября 2026 г.

9 лучших навыков для Codex для дизайна сайтов в 2026 году
17 сентября 2026 г.

8 лучших навыков для Claude для дизайна сайтов в 2026 году
17 сентября 2026 г.
Комментарии(0)
Оставьте комментарий
Войдите, чтобы присоединиться к обсуждению