RE:NODE

Эксплуатация11 мин чтения

S3: path-style и virtual-hosted URL, и что такое регион

Что такое path-style и virtual-hosted адресация в S3, почему собственные endpoint используют path-style и какая именно настройка нужна в каждом популярном SDK и инструменте.

0 прочтений

Клиент S3 может поместить имя бакета в одно из двух мест. Path-style кладёт его в путь: https://s3.example.com/my-bucket/photo.jpg. Virtual-hosted style кладёт его в имя хоста: https://my-bucket.s3.example.com/photo.jpg. Amazon предпочитает второй вариант, и большинство SDK выбирают его по умолчанию, когда работают с AWS. Почти любой S3-совместимый endpoint, который не AWS - собственное хранилище, небольшой провайдер, сервер хранения за одним доменом, - хочет первый, потому что virtual-hosted адресации нужны DNS-запись и TLS-сертификат для каждого имени бакета. Если клиент настроен не на тот стиль, вы получаете ошибки DNS, ошибки сертификата или «бакет не найден», а исправляется это одной настройкой. В этой статье объясняются оба стиля, что на самом деле делает имя региона и где эта единственная настройка находится в каждом инструменте, которым вы, скорее всего, пользуетесь.

Два стиля адресации#

Каждый запрос к S3 называет три вещи: endpoint (какой сервер), бакет (какой контейнер) и ключ (какой объект). Ключ всегда идёт в путь. Вопрос только в том, куда идёт бакет.

СтильЗапрос для бакета media, ключ 2026/cat.jpgЧто нужно
Path-styleGET https://s3.example.com/media/2026/cat.jpgОдно имя хоста, один сертификат
Virtual-hostedGET https://media.s3.example.com/2026/cat.jpgDNS-имя и сертификат, покрывающие каждый бакет

На уровне протокола разница невелика. В path-style заголовок Host - это endpoint, а первый сегмент пути - бакет. В virtual-hosted style заголовок Host несёт бакет как поддомен, а путь начинается с ключа. Подпись покрывает и то и другое, поэтому клиент не может сменить стиль на полпути: он подписывает запрос под тот стиль, в котором собирается его отправить.

code
# Path-stylePUT /media/2026/cat.jpg HTTP/1.1Host: s3.example.com# Virtual-hosted stylePUT /2026/cat.jpg HTTP/1.1Host: media.s3.example.com

Сервер, который понимает только path-style, получает virtual-hosted запрос, видит Host: media.s3.example.com и либо не получает его вовсе (для этого имени нет DNS-записи), либо принимает первый сегмент пути, 2026, за бакет. Отсюда и берутся странные ошибки «NoSuchBucket: 2026».

Почему AWS перешёл на virtual-hosted, а остальные - нет#

Path-style - исходный формат URL в S3. Amazon стал предпочитать virtual-hosted style, потому что бакет в имени хоста позволяет DNS маршрутизировать каждый бакет независимо: бакет может жить в другом регионе или на других фронтенд-кластерах, а клиент, разрешающий media.s3.amazonaws.com, попадает куда нужно без лишнего перехода. В 2019 году AWS объявил, что path-style запросы для новых бакетов будут выведены из эксплуатации, а после недовольства пользователей отложил это изменение на неопределённый срок. Path-style в AWS по-прежнему работает, но каждая новая функция и каждый пример AWS предполагают virtual-hosted style - поэтому SDK и выбирают его по умолчанию.

Для S3-совместимого endpoint, который представляет собой один сервер, выгода получается обратной. Virtual-hosted адресации нужны:

  • Wildcard DNS-запись *.s3.example.com, указывающая на сервер, чтобы разрешалось любое имя бакета.
  • Wildcard TLS-сертификат на *.s3.example.com. Let's Encrypt выдаёт wildcard-сертификаты только через проверку DNS-01, а значит, системе выпуска нужно дать доступ к вашей DNS-зоне.
  • Сервер, настроенный на свой базовый домен, чтобы он знал, что бакет нужно брать из заголовка Host.

Path-style нужна одна обычная A-запись и один обычный сертификат. Имена бакетов никогда не касаются DNS. Поэтому честный выбор по умолчанию для собственного хранилища или хранилища с одним endpoint - path-style, и поэтому в документации почти каждого такого продукта где-то в начале написано «включите force path style».

Есть и ещё одна практическая причина. Имена бакетов могут содержать точки (backups.example.org - допустимое имя). В virtual-hosted style это превращается в backups.example.org.s3.example.com, а wildcard-сертификат на *.s3.example.com его не покрывает, потому что wildcard совпадает ровно с одной меткой. У path-style такой проблемы нет.

Что делает имя региона#

Клиенты S3 спрашивают регион, даже если вы работаете не с AWS, и люди вполне резонно не понимают, что туда вписать.

В AWS регион выполняет две задачи. Он выбирает endpoint (s3.eu-central-1.amazonaws.com) и входит в область учётных данных Signature Version 4: каждый подписанный запрос содержит строку вроде 20261008/eu-central-1/s3/aws4_request, и сервер пересчитывает подпись с тем регионом, который ожидает. Если они расходятся, AWS отвечает AuthorizationHeaderMalformed и сообщает, какой регион ему был нужен.

С собственным endpoint первая задача исчезает - endpoint вы задаёте сами. Вторая остаётся: регион по-прежнему зашит в подпись. Важно, чтобы сервер принимал регион, с которым подписал клиент. Некоторые S3-совместимые серверы настроены на одно конкретное имя региона и отвергают всё остальное; другие принимают любое значение. Когда хранилище принимает любой регион, обычно выбирают us-east-1: именно на него откатывается большинство инструментов, когда ничего не задано, и это избавляет от целого класса странностей SDK, где пустой регион считается ошибкой.

Регион появляется ещё в одном месте, где может укусить: некоторые SDK, получив регион без endpoint, строят из него URL AWS. Если вы видите, что клиент пытается достучаться до s3.us-east-1.amazonaws.com, значит, не подхватилась настройка endpoint, а не региона.

Имена бакетов и откуда взялись правила#

Правила именования бакетов S3 выглядят произвольными, пока не прочитаешь их как правила DNS, которыми они и являются. Поскольку бакет когда-нибудь может адресоваться как имя хоста, правила Amazon требуют имён, которые являются допустимыми DNS-метками:

  • Длина от 3 до 63 символов.
  • Только строчные буквы, цифры, дефисы и точки.
  • Начинается и заканчивается буквой или цифрой.
  • Не похоже на IP-адрес (192.168.1.10 - недопустимое имя бакета).
  • Никаких двух точек подряд и точки рядом с дефисом.

S3-совместимые серверы обычно требуют тех же правил, даже если используют только path-style, потому что на них рассчитаны клиенты и инструменты. Ограничьтесь строчными буквами, цифрами и дефисами - game-backups, media-prod, db-dumps-2026, - и вы никогда не встретите инструмент, который откажется от такого имени. Точки не используйте вовсе: на path-style endpoint они ничего не дают, а если вы когда-нибудь перейдёте на virtual-hosted, вызовут описанную выше проблему с сертификатом.

Имена бакетов к тому же видны всем. Они появляются в каждом URL, каждой presigned-ссылке и каждой строке лога. Не вписывайте в них имя клиента, секрет или внутреннее кодовое название проекта.

Настройка в каждом популярном инструменте#

Эту часть стоит сохранить в закладки. Каждая строка ниже заставляет клиент использовать path-style с собственным endpoint.

Инструмент или SDKНастройка path-styleНастройка endpoint
AWS CLIs3 = addressing_style = path в ~/.aws/config--endpoint-url или endpoint_url
boto3 (Python)Config(s3={'addressing_style': 'path'})endpoint_url=
AWS SDK для JavaScript v3forcePathStyle: trueendpoint:
AWS SDK для Go v2o.UsePathStyle = trueo.BaseEndpoint
AWS SDK для Java v2.forcePathStyle(true).endpointOverride(URI)
AWS SDK для PHP / Laravel'use_path_style_endpoint' => true'endpoint' =>
rcloneforce_path_style = true (по умолчанию)endpoint =
restic-o s3.bucket-lookup=pathв URL репозитория
Cyberduckпрофиль «S3 (Deprecated path style requests)»поле сервера
WinSCPAdvanced, Environment, S3, URL style: Pathполе хоста

Некоторые из них заслуживают пояснения.

AWS CLI берёт стиль из файла конфигурации. Вложенный блок s3 - та часть, которую обычно упускают:

~/.aws/config
[profile store]region = us-east-1endpoint_url = https://s3.example.coms3 =  addressing_style = path

endpoint_url на верхнем уровне профиля поддерживается начиная с AWS CLI 2.13; в более старых версиях --endpoint-url передаётся в каждой команде. Полная настройка - в статье хранилище S3 через AWS CLI.

В boto3 стиль задаётся в объекте botocore.config.Config:

python
import boto3from botocore.config import Configs3 = boto3.client(    "s3",    endpoint_url="https://s3.example.com",    region_name="us-east-1",    aws_access_key_id="AKIA...",    aws_secret_access_key="...",    config=Config(s3={"addressing_style": "path"}, signature_version="s3v4"),)

В JavaScript SDK v3 forcePathStyle передаётся в конструктор клиента:

javascript
import { S3Client } from "@aws-sdk/client-s3";const s3 = new S3Client({  endpoint: "https://s3.example.com",  region: "us-east-1",  forcePathStyle: true,  credentials: { accessKeyId: "AKIA...", secretAccessKey: "..." },});

Laravel открывает эту опцию PHP SDK через свой конфиг файловых систем, обычно управляемый строкой AWS_USE_PATH_STYLE_ENDPOINT=true в .env. В rclone force_path_style для S3-remote и так по умолчанию true - это одна из причин, почему он обычно с первого раза работает с необычными endpoint. restic по умолчанию использует auto, то есть virtual-hosted для endpoint Amazon и Google и path-style для всего остального, так что ему обычно тоже не нужен флаг; опция существует на случай, если автоопределение ошибётся. Примеры кода для обоих SDK, включая загрузку и листинг, - в статье S3 из Node.js и Python.

Как выглядит неправильный стиль#

Симптомы узнаваемы, если хоть раз их видели.

СимптомВероятная причина
Could not connect to the endpoint URL: https://media.s3.example.com/Virtual-hosted style, нет DNS для поддомена бакета
getaddrinfo ENOTFOUND media.s3.example.comТо же самое, из Node
Ошибка сертификата с именем media.s3.example.comVirtual-hosted style; сертификат покрывает только s3.example.com
NoSuchBucket с именем первой папки вашего ключаСервер прочитал путь как бакет/ключ; клиент отправил virtual-hosted
Cyberduck: «Cannot read container configuration»Стандартный профиль S3 против сервера только с path-style
SignatureDoesNotMatch после смены одного лишь стиляProxy переписал Host или клиент закэшировал старый стиль

Первые два встречаются чаще всего и узнаются проще всего: посмотрите на имя хоста в ошибке. Если оно начинается с имени вашего бакета, клиент использует virtual-hosted style, и ему нужна настройка из таблицы выше.

Последний тоньше. Подпись покрывает заголовок Host. Если reverse proxy перед хранилищем пересылает запросы с другим Host, чем использовал клиент, каждая подпись не сходится, хотя учётные данные верные. Proxy перед S3 endpoint должен передавать исходный Host без изменений.

Одну ошибку часто списывают на стиль адресации, хотя он тут ни при чём. С начала 2025 года AWS CLI (с версии 2.23) и текущие AWS SDK по умолчанию добавляют к загрузкам контрольные суммы на основе CRC. Некоторые S3-совместимые серверы их не принимают, и сбой проявляется как SignatureDoesNotMatch, MissingContentLength или жалоба на контрольную сумму при PutObject, тогда как листинг и скачивание работают нормально. Исправляют это две настройки, которые заставляют клиент отправлять контрольные суммы только тогда, когда операция их требует: request_checksum_calculation = when_required и response_checksum_validation = when_required в профиле конфигурации AWS или аналогичные опции в SDK. Если загрузки падают, а чтение работает, попробуйте это прежде всего остального.

Path-style, HTTPS и ваш собственный домен#

Path-style endpoint - это всего одно имя хоста, поэтому поставить перед ним HTTPS - та же задача, что и для любого веб-сервиса: A-запись, указывающая на proxy, сертификат на это имя и proxy, пересылающий запросы на порт хранилища с обычным HTTP. Механика описана в статье что делает reverse proxy, а сторона DNS - в статье ваш домен и его сертификат.

Именно так устроено хранилище S3 у RE:NODE. Endpoint отвечает по обычному HTTP на своём порту, а в каждом тарифе есть proxy-слот: направьте имя хоста, например s3.example.com, на показанный адрес, и сертификат будет выпущен и продлеваться за вас. После этого клиенты используют https://s3.example.com с path-style адресацией и любым именем региона. Обычный HTTP на голом порту годится для проверки, но тела запросов передаются без шифрования, поэтому всё, что уходит за пределы вашей машины, должно идти через HTTPS-имя.

PUT /media/2026/cat.jpgHost сохраняетсяS3-клиентpath-style, любой регионProxy-слотhttps://s3.example.comS3 endpointобычный HTTP на своём портуБакетmedia
Path-style запрос через proxy-слот

Поскольку бакет никогда не появляется в имени хоста, один сертификат покрывает все бакеты, которые вы когда-либо создадите, и при добавлении нового ничего не нужно менять в DNS.

Presigned URL и публичные ссылки#

Стиль адресации определяет и вид URL, которые вы раздаёте другим людям. Presigned URL, сгенерированный path-style клиентом, выглядит как https://s3.example.com/media/2026/cat.jpg?X-Amz-Algorithm=...&X-Amz-Signature=..., и имя хоста входит в подписанные данные. Отсюда два следствия:

  • Генерируйте presigned URL с тем же endpoint, который будет использовать браузер. URL, подписанный для http://203.0.113.10:PORT и затем вручную переписанный на https://s3.example.com, не пройдёт проверку подписи.
  • Если позже вы перейдёте с path-style на virtual-hosted (или к другому провайдеру), старые presigned URL перестанут работать. Они в любом случае истекают, но долгоживущие ссылки в письмах сломаются.

Срок действия, загрузки через PUT из браузера и CORS разобраны в статье presigned URL для загрузки и скачивания.

Выбор стиля для собственного кода#

Если вы пишете код, который работает с S3, делайте endpoint и стиль конфигурацией, а не константами. Небольшой блок настроек с S3_ENDPOINT, S3_REGION, S3_BUCKET и S3_FORCE_PATH_STYLE позволяет одному и тому же коду работать с AWS в одном окружении и с собственным хранилищем в другом, а смена провайдера сводится к изменению четырёх переменных. Этот подход описан в статье переменные окружения и секреты.

.env
S3_ENDPOINT=https://s3.example.comS3_REGION=us-east-1S3_BUCKET=mediaS3_FORCE_PATH_STYLE=trueS3_ACCESS_KEY_ID=AKIA...S3_SECRET_ACCESS_KEY=...

Прежде чем подключать новый endpoint к приложению, проверьте его из командной строки. Три команды разом отвечают на все вопросы о стиле, регионе и учётных данных:

bash
$ aws s3 ls --profile store$ aws s3 cp ./test.txt s3://media/test.txt --profile store$ aws s3 ls s3://media/ --profile store --debug 2>&1 | grep -i "host"

Первая показывает список бакетов, что подтверждает endpoint и ключи. Вторая записывает объект, что подтверждает подпись запроса с телом. Третья печатает детали запроса; строка Host показывает, какой стиль клиент использовал на самом деле, - это самый быстрый способ убедиться, что изменение конфигурации вступило в силу. Если все три работают в CLI, а приложение всё равно падает, значит, SDK приложения не получает тех же настроек.

Причин предпочесть virtual-hosted style на endpoint не от AWS почти не бывает, если только провайдер этого не требует. Path-style проще закрыть TLS, он переживает имена бакетов с точками и поддерживается любым S3-совместимым клиентом. А в самом AWS оставьте значение SDK по умолчанию.

FAQ#

Path-style устарел?

В AWS он не рекомендуется, и когда-то его даже собирались удалить, но удаление отложили, и path-style запросы по-прежнему работают. На S3-совместимых endpoint это обычный, поддерживаемый режим. Слово «deprecated» в меню некоторых инструментов отражает позицию AWS, а не состояние протокола.

Какой регион указывать для S3-совместимого хранилища?

Тот, что называет провайдер. Если он принимает любое имя, используйте us-east-1 - это значение, на которое рассчитывает большинство инструментов, и оно меньше всего рискует споткнуть SDK. Регион должен лишь совпадать у клиента и сервера.

Можно ли указать IP-адрес в качестве endpoint?

Да, но только с path-style. Virtual-hosted запросу понадобилось бы имя хоста вида bucket.203.0.113.10, а это недопустимое имя. Для всего, что сложнее локальной проверки, используйте имя хоста с HTTPS, чтобы трафик был зашифрован.

Почему тот же ключ работает в rclone, но не в моём приложении?

rclone по умолчанию использует path-style, а большинство SDK - virtual-hosted. Если учётные данные и endpoint одинаковые, разница почти всегда в стиле адресации. Включите опцию path-style в SDK.

Влияет ли стиль адресации на производительность?

Не в хранилище с одним endpoint. Оба стиля приводят к одному и тому же серверу; разница лишь в том, какой заголовок несёт имя бакета. В AWS virtual-hosted style позволяет маршрутизировать запросы напрямую - отчасти поэтому Amazon его и предпочитает.


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000