Middleware-режим для интеграции с Express и Fastify

Механизм server.fs управляет доступом dev-сервера Vite к файловой системе. Во время разработки Vite активно работает с локальными файлами: загружает модули, обрабатывает зависимости, читает конфигурацию, импортирует ресурсы и обслуживает содержимое проекта через HTTP. Без ограничений такой доступ представлял бы серьёзную угрозу безопасности.

Секция server.fs позволяет:

  • ограничивать область доступа сервера;
  • запрещать чтение чувствительных директорий;
  • контролировать импорт файлов вне корня проекта;
  • управлять безопасностью в monorepo;
  • разрешать или блокировать конкретные пути.

Назначение server.fs

По умолчанию Vite разрешает доступ только к определённым директориям проекта. Попытка обратиться к файлам за пределами допустимой области приводит к ошибке:

403 Restricted

Это особенно заметно при:

  • импорте файлов из соседних директорий;
  • использовании monorepo;
  • подключении shared-пакетов;
  • работе через symbolic links;
  • вынесении ресурсов за пределы root.

Пример проблемы:

import config from '../. ./shared/config.js'

Если каталог shared находится вне разрешённой области, Vite заблокирует доступ.


Структура server.fs

Конфигурация располагается внутри секции server.

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    fs: {

    }
  }
})

Доступны параметры:

Параметр Назначение
allow список разрешённых директорий
deny список запрещённых файлов
strict режим строгой проверки доступа

Параметр server.fs.strict

Общий принцип работы

strict включает строгую файловую изоляцию.

export default defineConfig({
  server: {
    fs: {
      strict: true
    }
  }
})

При активном режиме Vite:

  • запрещает произвольный доступ к файловой системе;
  • ограничивает чтение файлов;
  • проверяет принадлежность пути к разрешённым каталогам.

Поведение при strict: true

Разрешается доступ:

  • к root проекта;
  • к workspace root;
  • к путям из allow.

Запрещается:

  • чтение произвольных директорий;
  • импорт файлов выше root;
  • доступ к системным путям.

Отключение строгого режима

export default defineConfig({
  server: {
    fs: {
      strict: false
    }
  }
})

После отключения ограничения существенно ослабляются.

Последствия

Dev-сервер получает возможность читать:

  • соседние каталоги;
  • внешние модули;
  • произвольные локальные файлы.

Это может привести к:

  • утечке конфиденциальных данных;
  • доступу к .env;
  • публикации системных файлов;
  • раскрытию внутренней структуры проекта.

Когда используется strict: false

Отключение иногда применяется:

  • в старых monorepo;
  • во внутренних корпоративных инструментах;
  • при миграции legacy-проектов;
  • в нестандартных окружениях.

Для обычной разработки рекомендуется сохранять strict: true.


Параметр server.fs.allow

Разрешение внешних директорий

allow добавляет дополнительные пути, доступные dev-серверу.

export default defineConfig({
  server: {
    fs: {
      allow: ['..']
    }
  }
})

Теперь Vite сможет читать файлы уровнем выше текущего проекта.


Разрешение конкретной директории

import path from 'path'

export default defineConfig({
  server: {
    fs: {
      allow: [
        path.resolve(__dirname, '../shared')
      ]
    }
  }
})

Использование в monorepo

Типичная структура:

workspace/
├── apps/
│   └── frontend/
├── packages/
│   └── ui/

Конфигурация:

import path from 'path'

export default defineConfig({
  server: {
    fs: {
      allow: [
        path.resolve(__dirname, '../. ./packages')
      ]
    }
  }
})

Теперь приложение может импортировать:

import { Button } from '../. ./packages/ui'

Несколько разрешённых директорий

fs: {
  allow: [
    '/shared',
    '/configs',
    '/packages'
  ]
}

Использование абсолютных путей

Vite корректно работает с абсолютными путями:

fs: {
  allow: [
    'D:/workspace/shared'
  ]
}

На Linux:

fs: {
  allow: [
    '/home/dev/shared'
  ]
}

Автоматическое определение workspace root

Vite умеет автоматически находить workspace root.

Поддерживаются:

  • pnpm-workspace.yaml
  • lerna.json
  • package.json с workspaces

Например:

repo/
├── package.json
├── apps/
├── packages/

В этом случае Vite может автоматически разрешить workspace-уровень без ручного указания allow.


Параметр server.fs.deny

Назначение

deny запрещает доступ к определённым файлам даже при наличии разрешений.

export default defineConfig({
  server: {
    fs: {
      deny: ['.env', '.env.*']
    }
  }
})

Защита переменных окружения

Vite по умолчанию блокирует доступ к:

  • .env
  • .env.local
  • .env.production
  • сертификатам;
  • приватным ключам.

Это предотвращает случайную утечку конфиденциальных данных через браузер.


Пользовательские ограничения

fs: {
  deny: [
    '*.pem',
    '*.key',
    '*.crt'
  ]
}

Блокировка директорий

fs: {
  deny: [
    'secret/**'
  ]
}

Как работает проверка доступа

При запросе файла Vite:

  1. нормализует путь;
  2. проверяет symbolic links;
  3. сопоставляет путь с deny;
  4. проверяет соответствие allow;
  5. возвращает файл либо ошибку 403.

Связь server.fs и импорта модулей

Ограничения применяются ко всем типам загрузки:

  • JavaScript;
  • TypeScript;
  • CSS;
  • JSON;
  • изображениями;
  • SVG;
  • WASM;
  • raw imports.

Работа с symbolic links

Структура:

project/
├── node_modules/
└── linked-package -> ../shared-package

Даже если symlink находится внутри проекта, физический путь может располагаться вне разрешённой области.


Решение

import path from 'path'

export default defineConfig({
  server: {
    fs: {
      allow: [
        path.resolve(__dirname, '../shared-package')
      ]
    }
  }
})

Использование с Docker

Типичная проблема

При работе через Docker:

  • root контейнера отличается;
  • mounted volumes имеют нестандартные пути;
  • Vite может блокировать директории.

Конфигурация

fs: {
  allow: [
    '/app',
    '/workspace'
  ]
}

Использование с WSL

В WSL пути Linux и Windows отличаются.

Пример:

fs: {
  allow: [
    '/mnt/c/projects/shared'
  ]
}

Разница между root и server.fs.allow

root

Определяет:

  • базовую директорию проекта;
  • точку поиска index.html;
  • контекст dev-сервера.

allow

Определяет:

  • дополнительные доступные пути;
  • разрешения на чтение файлов.

Ошибка 403 Restricted

Типичный пример

The request url is outside of Vite serving allow list.

Причины

Импорт вне root

import file from '../. ./. ./config.js'

Не настроен allow

fs: {
  allow: []
}

Работа в monorepo

Vite не всегда корректно определяет workspace root.


Физический путь находится вне разрешённой области.


Диагностика проблем

Проверка абсолютного пути

console.log(
  path.resolve(__dirname, '../shared')
)

Linux/macOS:

readlink -f node_modules/package

Windows PowerShell:

Get-Item node_modules/package

Временное отключение strict

fs: {
  strict: false
}

Если проблема исчезла — причина в ограничениях доступа.


Практическая конфигурация для monorepo

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig({
  server: {
    fs: {
      strict: true,
      allow: [
        path.resolve(__dirname, '../. ./packages'),
        path.resolve(__dirname, '../. ./shared')
      ],
      deny: [
        '.env',
        '.env.*',
        '*.pem',
        '*.key'
      ]
    }
  }
})

Влияние на безопасность

Потенциальные угрозы без ограничений

При неправильной настройке возможно:

  • чтение приватных файлов;
  • утечка SSH-ключей;
  • доступ к сертификатам;
  • раскрытие .env;
  • публикация внутренней инфраструктуры.

Особенно опасны

id_rsa
.env
database.yml
config.production.json

Рекомендации по настройке

Использование минимального списка allow

Плохо:

allow: ['..']

Лучше:

allow: [
  '/shared/ui'
]

Не отключать strict без необходимости

strict: true

должен оставаться стандартным режимом.


Ограничение чувствительных файлов

deny: [
  '*.pem',
  '*.key',
  '.env*'
]

Использование абсолютных путей

Абсолютные пути уменьшают вероятность ошибок.


Поведение в production

Параметры server.fs работают только во время разработки.

На production-сборку они не влияют.

Команда:

vite build

не использует ограничения dev-сервера.


Совместимость с плагинами

Некоторые плагины:

  • читают внешние директории;
  • работают с monorepo;
  • генерируют виртуальные модули.

В таких случаях требуется корректный allow.

Пример:

fs: {
  allow: [
    '/packages',
    '/generated'
  ]
}

Внутренний механизм Vite

Во время обработки запроса Vite использует:

  • path normalization;
  • проверку real path;
  • фильтрацию deny-list;
  • проверку разрешённых root;
  • sandbox-модель доступа.

Это делает dev-сервер существенно безопаснее обычного статического файлового сервера.


Полная конфигурация

import { defineConfig } from 'vite'
import path from 'path'

export default defineConfig({
  server: {
    fs: {
      strict: true,

      allow: [
        path.resolve(__dirname, '../shared'),
        path.resolve(__dirname, '../packages/ui')
      ],

      deny: [
        '.env',
        '.env.*',
        '*.pem',
        '*.key',
        '*.crt'
      ]
    }
  }
})