Что такое виртуальный хост (virtual host) и как он реализуется в Node.js?

Ответ

Виртуальный хостинг (virtual hosting) — это метод, позволяющий обслуживать несколько доменных имен (например, `api.example.com` и `admin.example.com`) или независимых веб-сайтов на одном физическом сервере с одним IP-адресом и одним запущенным экземпляром приложения Node.js. Механизм основан на анализе HTTP-заголовка `Host`, который клиент отправляет в каждом запросе (это требование HTTP/1.1). **Как это работает:** 1. DNS для обоих доменов (`api.example.com` и `admin.example.com`) указывает на один IP-адрес сервера. 2. Сервер Node.js получает HTTP-запрос. 3. Приложение проверяет значение `req.headers.host`. 4. В зависимости от значения, запрос маршрутизируется к соответствующей логике или даже к другому внутреннему приложению (микросервису). **Базовая реализация на чистом Node.js `http` модуле:** ```javascript const http = require('http'); const apiApp = (req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ service: 'API', version: '1.0' })); }; const adminApp = (req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end('

Admin Dashboard

'); }; const defaultApp = (req, res) => { res.writeHead(404); res.end('Virtual host not found'); }; const server = http.createServer((req, res) => { const host = req.headers.host; // Маршрутизация на основе заголовка Host switch (host) { case 'api.example.local:3000': case 'api.example.com': return apiApp(req, res); case 'admin.example.local:3000': case 'admin.example.com': return adminApp(req, res); default: return defaultApp(req, res); } }); server.listen(3000, () => { console.log('Virtual host server running on port 3000'); console.log('Try: http://api.example.local:3000 or http://admin.example.local:3000'); }); ``` **Использование специализированного middleware для Express.js:** Для Express популярным решением является middleware `vhost`. Установите его: `npm install vhost`. ```javascript const express = require('express'); const vhost = require('vhost'); // Создаем отдельные экземпляры приложений const apiApp = express(); apiApp.get('/', (req, res) => res.json({ app: 'API' })); const blogApp = express(); blogApp.get('/', (req, res) => res.send('Blog Homepage')); // Основное приложение const mainApp = express(); // Подключаем виртуальные хосты mainApp.use(vhost('api.example.com', apiApp)); mainApp.use(vhost('blog.example.com', blogApp)); // Обработчик для всех остальных хостов mainApp.use((req, res) => { res.status(404).send('Host not configured'); }); mainApp.listen(3000); ``` **Современный подход с прокси-сервером (Nginx, Caddy, Traefik):** В production-среде виртуальный хостинг чаще реализуется на уровне обратного прокси-сервера перед Node.js приложением. Это более эффективно и гибко. **Пример конфигурации Nginx:** ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:3001; # Наше Node.js API приложение proxy_set_header Host $host; } } server { listen 80; server_name admin.example.com; location / { proxy_pass http://localhost:3002; # Наше Node.js Admin приложение proxy_set_header Host $host; } } ``` В этом случае каждое Node.js-приложение (`:3001`, `:3002`) может быть независимым и даже написано на разных фреймворках, а Nginx берет на себя маршрутизацию на основе доменного имени. **Применение виртуальных хостов:** * **Мультитенантные приложения (SaaS):** `client1.yourapp.com`, `client2.yourapp.com`. * **Разделение сервисов:** `api.domain.com`, `docs.domain.com`, `app.domain.com`. * **Стенды для тестирования:** `staging.domain.com`, `qa.domain.com`.

Ответ 18+ 🔞

Давай разберем, что это за виртуальный хостинг такой, а то звучит сложно, а на деле — проще пареной репы, если не вникать в дебри. Представь себе: есть у тебя один сервер, один IP-адрес, как одинокий мужик в трёшке. И на него лезут гости с разных адресов: кто с `api.example.com`, кто с `admin.example.com`. А сервер смотрит на них и думает: «Ёпта, а вы вообще кто такие?» И тут он смотрит в паспорт — то есть в заголовок `Host`, который каждый браузер или клиент обязан предъявить. И по этому паспорту уже решает, куда запрос направить: в гостиную к API или на кухню к админке. Весь механизм, блядь, на этом и держится — `req.headers.host`. Без заголовка `Host` — это как человек без лица, нихуя не понятно. **Как это в лоб на чистом Node.js делается:** Смотри, тут просто до безобразия. Создаёшь разные обработчики, как разные квартиры в одной хрущёвке, и по пришедшему заголовку раскидываешь гостей. ```javascript const http = require('http'); const apiApp = (req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ service: 'API', version: '1.0' })); }; const adminApp = (req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end('

Admin Dashboard

'); }; const defaultApp = (req, res) => { res.writeHead(404); res.end('Virtual host not found'); }; const server = http.createServer((req, res) => { const host = req.headers.host; // Смотрим, кто пришёл, и отправляем куда надо switch (host) { case 'api.example.local:3000': case 'api.example.com': return apiApp(req, res); case 'admin.example.local:3000': case 'admin.example.com': return adminApp(req, res); default: return defaultApp(req, res); } }); server.listen(3000, () => { console.log('Virtual host server running on port 3000'); console.log('Try: http://api.example.local:3000 or http://admin.example.local:3000'); }); ``` Всё, блядь, элементарно. Но если ты на Express сидишь, как нормальный человек, то там есть готовый костыль — `vhost`. Ставишь его (`npm install vhost`) и получаешь красивую штуку. ```javascript const express = require('express'); const vhost = require('vhost'); // Делаем два отдельных приложения, как сиамских близнецов const apiApp = express(); apiApp.get('/', (req, res) => res.json({ app: 'API' })); const blogApp = express(); blogApp.get('/', (req, res) => res.send('Blog Homepage')); // Основное приложение — оно как швейцар у подъезда const mainApp = express(); // Вешаем на него виртуальные хосты mainApp.use(vhost('api.example.com', apiApp)); mainApp.use(vhost('blog.example.com', blogApp)); // А если припёрся с левым адресом — получи в лоб 404 mainApp.use((req, res) => { res.status(404).send('Host not configured'); }); mainApp.listen(3000); ``` Но слушай сюда, самый сок не в этом. В реальной жизни, на проде, эту всю хуйню выносят на отдельный прокси-сервер, типа Nginx. Это, блядь, как поставить у входа здорового вышибалу, который сразу сортирует: кому куда. И твои Node.js-приложения тогда вообще об этом не парятся, они просто работают на своих портах. **Вот как этот вышибала (Nginx) выглядит:** ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:3001; # Вот твоё API на 3001 порту proxy_set_header Host $host; } } server { listen 80; server_name admin.example.com; location / { proxy_pass http://localhost:3002; # А вот админка, уже на 3002 proxy_set_header Host $host; } } ``` Красота, да? Каждое приложение живёт отдельно, одно упало — другому похуй. И масштабируется это всё гораздо проще. **Вот где это применяется, чтобы ты понимал масштаб:** * **Разные клиенты в SaaS:** `client1.yourapp.com`, `client2.yourapp.com` — один код, а базы разные, и все довольны. * **Разделение сервисов:** `api.site.com`, `docs.site.com` — чтобы не превращать всё в одно сплошное месиво. * **Тестовые стенды:** `staging.site.com`, `qa.site.com` — чтобы прод не ёбнуть случайно кривым кодом. В общем, идея проще некуда: один IP, много имён, а внутри уже решаем, кто куда идёт. Главное — не запутаться, а то получится **манда с ушами**, когда всё свалено в одну кучу.