Чтобы веб-сайт мог безопасно загружать изображения, шрифты или другие файлы из S3-хранилища, расположенного на другом домене, необходимо корректно настроить механизм CORS. В этом руководстве мы простыми словами объясним, что такое CORS, как он работает в связке с S3 на примере нашего хранилища и как его правильно настроить в сервисе FirstVDS для корректного обмена ресурсами.
В этой статье вы найдете базовые принципы CORS — зачем он нужен и как работает между браузером и S3-хранилищем, а также детальный разбор заголовков — какие заголовки отправляет браузер и как отвечает S3. Кроме того, дадим пошаговую инструкцию по настройке CORS в нашем S3 Manager для типичных задач и разберем распространенные ошибки со способами их исправления.
Что такое CORS и зачем он нужен при работе с S3
CORS (Cross-Origin Resource Sharing) — это механизм безопасности, реализованный в современных браузерах. Он определяет правила доступа к ресурсам между различными доменами и предотвращает несанкционированные междоменные (cross-domain) запросы.
По умолчанию браузеры блокируют такие запросы, чтобы запретить сторонним сайтам получать данные без явного разрешения. Однако при работе с S3-совместимыми хранилищами часто требуется предоставить доступ к объектам из разных источников. Например, когда файлы хранятся в бакете, а сайт, который их использует, размещён на другом домене.
Как работает CORS
Браузер по умолчанию защищает данные пользователя. И если веб-приложение находится на одном домене, а обращается за данными на другой, в силу вступает ограничение Same-Origin Policy. Иначе говоря, политика одного источника.
Такая политика применяется для того, чтобы злоумышленник не смог с вредоносной страницы украсть, например, ваши файлы из S3 или другую конфиденциальную информацию.
CORS — это механизм, который может разрешить обмен данными между доменами, когда это действительно необходимо. Он определяет, какие именно источники, методы и заголовки могут быть использованы при обращении к ресурсу.
Чтобы CORS работал корректно, необходимо выполнить два условия:
- разрешить доступ к объекту на уровне S3 (через ACL или политику бакета),
- явно указать в настройках CORS бакета, с каких доменов, какими методами и с какими заголовками браузеру разрешено обращаться к этим объектам.
Если хотя бы одно из этих условий не выполнено, браузер либо заблокирует ответ по CORS, либо S3 вернёт ошибку доступа (403).
Какие типы запросов бывают в CORS
Работа механизма CORS строится на двух типах запросов: простых и предварительных.
Простой запрос (Simple Request)
Выполняется сразу, если запрос использует «разрешённые» методы (GET, HEAD или POST с безопасными заголовками).
В ответ сервер добавляет заголовок:
Access-Control-Allow-Origin: <домен | *>
Если в нём указан запрашивающий домен или символ *, браузер допускает обработку ответа.
Предварительный запрос (Preflight Request)
Для запросов, использующих нестандартные методы (PUT, DELETE) или пользовательские заголовки, браузер предварительно выполняет запрос методом OPTIONS.
Цель — убедиться, что сервер разрешает такие обращения. В ответе он указывает допустимые параметры, например:
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Origin: https://example.com
Если условия совпадают, браузер выполняет основной запрос.
Так S3-хранилище сообщает браузеру, каким источникам и каким образом разрешено обращаться к данным.
Заголовки CORS в контексте S3‑хранилища
Когда объект (любой файл) запрашивается из S3-хранилища с другого домена, браузер и сервис S3 обмениваются специальными HTTP-заголовками, которые управляют кросс-доменным доступом.
S3-хранилище не выполняет логики приложения и не анализирует содержимое запросов — оно лишь проверяет настройки бакета и возвращает файлы. Поэтому правила CORS задаются не в коде сайта, а в конфигурации бакета. Если запрос соответствует этим правилам, S3 автоматически добавляет нужные CORS-заголовки в HTTP-ответ.
Теперь разберёмся, какие заголовки участвуют в этом «диалоге» между браузером и S3. Их можно условно разделить на три группы:
- заголовки, которые S3 возвращает в ответе (разрешения от хранилища);
- заголовки, которые браузер отправляет при проверке доступа;
- служебные заголовки, используемые при предварительных (preflight) запросах.
Рассмотрим их по очереди.
Заголовки в ответах S3
Access-Control-Allow-Origin
Главный заголовок, который определяет, каким источникам браузер разрешает использовать ответ. Если в конфигурации указано AllowedOrigins: ["*"], хранилище разрешает кросс-доменные запросы с любых доменов. Будьте аккуратны с этой настройкой: любой сайт сможет встраивать и использовать ваш контент в браузере пользователя. В большинстве случаев безопаснее явно указать конкретные домены, например:
"AllowedOrigins": ["https://example.com"]Access-Control-Allow-Methods
Отражает список методов, разрешённых для обращения к объектам в бакете.
Обычно достаточно GET для публичного чтения, но при загрузках или обновлениях могут понадобиться PUT или POST.
Пример:
"AllowedMethods": ["GET", "PUT"]Access-Control-Allow-Headers
Определяет, какие дополнительные HTTP-заголовки браузеру разрешено отправлять при запросе к объектам в бакете (например, Authorization или Content-Type). Если запрос выполняется с пользовательскими заголовками, браузер сначала делает предварительный запрос (OPTIONS) и спрашивает у S3, разрешено ли отправлять такие заголовки. Если хотя бы один заголовок не разрешён, браузер не отправит основной запрос, даже если сам объект доступен. Значение ["*"] означает, что браузеру разрешено отправлять любые заголовки. Это упрощает настройку, но при открытом доступе к бакету может быть небезопасно, поэтому в рабочих конфигурациях лучше явно перечислять только необходимые заголовки.
Access-Control-Expose-Headers
Позволяет клиентскому JavaScript-коду получать доступ к определённым HTTP-заголовкам ответа при кросс-доменном запросе. По умолчанию браузер скрывает большинство заголовков ответа, даже если сам запрос разрешён CORS. Этот заголовок используется в случаях, когда приложению необходимо работать не только с содержимым файла, но и с метаданными ответа. Например, доступ к заголовку Last-Modified позволяет определить, когда объект был изменён в последний раз:
"ExposeHeaders": ["Last-Modified"]Access-Control-Max-Age
Позволяет браузеру кэшировать результаты предварительных запросов (метод OPTIONS) и тем самым ускоряет повторные обращения. Например:
"MaxAgeSeconds": 3000
Заголовки, отправляемые браузером при обращении к S3-хранилищу
Origin
Указывает источник (домен), с которого идёт обращение. Именно его значение проверяет S3, сопоставляя с параметром AllowedOrigins из конфигурации бакета.
Access-Control-Request-Method и Access-Control-Request-Headers
Формируются браузером автоматически при предварительном (preflight) запросе методом OPTIONS и позволяют серверу определить, разрешён ли фактический запрос. Если запрашиваемые методы или заголовки не соответствуют правилам CORS бакета, S3 не добавит необходимые CORS-заголовки в ответ, и браузер заблокирует запрос, отображая ошибку CORS (например: “CORS header ‘Access-Control-Allow-Origin’ missing”).
Заголовки, которые клиент отправляет при предварительном запросе
Preflight-запрос (предварительный запрос) — это специальный CORS-запрос, который браузер автоматически отправляет перед некоторыми кросс-доменными запросами, чтобы заранее проверить, разрешает ли сервер использование запрашиваемого HTTP-метода и пользовательских заголовков.
Preflight-запрос выполняется методом OPTIONS и содержит следующие HTTP-заголовки:
Access-Control-Request-Method
Используется в предварительном (preflight) запросе и сообщает серверу, какой HTTP‑метод будет применён при фактическом запросе.
Access-Control-Request-Headers
Также используется при предварительном запросе — сообщает, какие пользовательские заголовки клиент собирается отправить.
Origin
Отправляется браузером автоматически во всех кросс‑доменных запросах и указывает источник (домен, схему и порт), с которого выполняется обращение. Именно этот заголовок сервер сравнивает со списком разрешённых источников в настройках CORS.
Пошаговая настройка CORS через S3 Manager и примеры других инструментов
CORS-настройки для S3-хранилища можно задать разными способами: через веб-интерфейс, с помощью консольных утилит или сторонних S3-клиентов. Все они управляют одной и той же конфигурацией бакета и дают одинаковый результат.
Далее подробно рассмотрим настройку CORS через S3 Manager FirstVDS — как наглядный и удобный способ. После этого кратко покажем альтернативные варианты настройки через CLI и другие инструменты.
Возможности S3 Manager
- Наш S3 полностью совместим с протоколом S3, поэтому можно использовать и другие инструменты, поддерживающие управление CORS, например, Cyberduck, s3cmd, S3 Browser
- Правила CORS могут быть как единичными, так и содержать несколько правил для разных источников\методов.
- Возможно комбинировать разрешения по разным методам, заголовкам и источникам в одной конфигурации.
- Применяемые правила с других клиентов отображаются в S3 Manager. Например, правила примененные из S3 Browser\s3cmd будут видны в S3 Manager.
Пример настройки через S3 Manager
Перейдем к практическому примеру настройки CORS для S3-хранилища через веб-интерфейс S3 Manager https://s3manager.firstvds.ru/.
Разберем распространенный сценарий, в котором веб-сайт https://example.com должен получать изображения из вашего бакета для отображения пользователям. Чтобы браузер разрешил такие междоменные запросы, необходимо настроить CORS-политику.
Перед настройкой определим цели:
- Предоставить доступ к бакету только для домена https://example.com.
- Разрешить исключительно чтение файлов (метод GET).
Далее покажем, как реализовать эту конфигурацию.
Конфигурация CORS
Заказываем услугу облачное хранилище s3 через личный кабинет или сайт:
Переходим в S3 Manager и создаём бакет:
Переходим к настройкам во вкладке CORS после создания бакета. Необходимо нажать на три точки и затем — Настройки:

Далее нужно перейти во вкладку CORS и нажать Добавить правило:

Теперь приступаем к конфигурации CORS. Задаем настройку с необходимым origin и методом:
2 — разрешенный origin
3 — подтверждаем конфигурацию
4 — сохраняем настройки
Затем положим в этот бакет одну картинку logo.png. Выбираем Загрузить и Файлы:

Выбираем файл для загрузки в S3, ждём завершения загрузки:

Файл загружен, можно закрыть окно:

Теперь, когда картинка загружена, сделаем бакет публичным. Обратите внимание, здесь используется настройка ACL (не Bucket policy):

Проверяем доступы у бакета. Если сейчас указать ссылку на эту картинку в коде сайта, то она отобразится.
Для тестирования написана страница для проверки CORS с тестового сайта.
Небольшой html\JS листинг:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Проверка CORS для изображений FirstVDS</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f9f9f9;
padding: 20px;
}
h1 {
color: #333;
}
input[type="text"] {
width: 70%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 8px 16px;
margin-left: 4px;
background: #83b235;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #83b210;
}
#result {
margin-top: 20px;
text-align: center;
}
#result img {
max-width: 90%;
border: 1px solid #ccc;
border-radius: 8px;
margin-top: 10px;
}
.error {
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<h1>Тест CORS для изображений</h1>
<p>Введите URL картинки (например: <code>https://.....</code>):</p>
<input type="text" id="imgUrl" value="https://firstvds.s3.firstvds.ru/logo.png">
<button id="checkBtn">Проверить</button>
<div id="result"></div>
<script>
const btn = document.getElementById("checkBtn");
const result = document.getElementById("result");
btn.addEventListener("click", async () => {
const url = document.getElementById("imgUrl").value.trim();
result.innerHTML = "Загружаем изображение...";
// Создаём Image для проверки CORS
const img = new Image();
img.crossOrigin = "anonymous"; // Попробуем запрос с CORS
img.onload = () => {
result.innerHTML = "";
result.appendChild(img);
};
img.onerror = () => {
result.innerHTML = `<p class="error">Ошибка загрузки. Возможно, CORS запрещён или URL неверный.</p>`;
};
img.src = url + (url.includes("?") ? "&" : "?") + "_ts=" + Date.now(); // добавим параметр для обхода кэша
});
</script>
</body>
</html>
Проверяем и видим, что проблем с доступом к файлу нет, ошибок по CORS также не наблюдается:

Если настройки CORS убрать, мы получим ожидаемую ошибку в DevTools консоли:

Таким образом, мы настроили CORS для отображения файла из S3 на сайте. Этот подход удобен для быстрого управления и визуального контроля настроек бакета. Все изменения применяются сразу при сохранении правил.
Все CORS-настройки бакета являются общими и сквозными: независимо от того, где они были заданы — через S3 Manager FirstVDS, CLI-утилиты или сторонние клиенты — они применяются одинаково и читаются любым S3-совместимым инструментом.
Ниже покажем, как уже настроенные через S3 Manager правила CORS выглядят при просмотре из консоли с помощью утилиты s3cmd и S3 Browser.
Проверка CORS-настроек через разные инструменты
На примере CLI (s3cmd)
Подключимся к нашему хранилищу:
~$ s3cmd --access_key=xxxxxxx --secret_key=xxxxxxx --region=Default --host=https://s3.firstvds.ru --host-bucket="https://s3.firstvds.ru/%(bucket)" --dump-config >~/.s3cfg
~$ s3cmd ls
2025-10-20 14:54 s3://firstvdsПроверим текущую конфигурацию бакета после настройки CORS и ACL из S3 Manager:
~$ s3cmd info s3://firstvds |xq
s3://firstvds/ (bucket):
Location: default
Payer: BucketOwner
Ownership: none
Versioning:none
Expiration rule: none
Block Public Access: none
Policy: {"Version":"2012-10-17","Statement":[{"Sid":"PublicListBucket","Effect":"Allow","Principal":"*","Action":"s3:ListBucket","Resource":"arn:aws:s3:::firstvds","Condition":{"StringEquals":{"s3:prefix":[""]}}},{"Sid":"PublicReadGetObject","Effect":"Allow","Principal":"*","Action":"s3:GetObject","Resource":"arn:aws:s3:::firstvds/*"}]}
CORS:<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<ID>example rule</ID>
<AllowedMethod>GET</AllowedMethod>
<AllowedOrigin>https://example.com</AllowedOrigin>
<MaxAgeSeconds>0</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>
ACL: *anon*: READ
ACL: S3 #16019072: FULL_CONTROL
URL: http://s3.firstvds.ru/firstvds/s3cmd позволяет подгрузить настройки CORS из xml файла. Например, создадим такую конфигурацию:
~$ cat cors.xml
<CORSConfiguration>
<CORSRule>
<ID>rules from CLI</ID>
<AllowedOrigin>https://example.com</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
<MaxAgeSeconds>6000</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>И применим ее к бакету:
~$ s3cmd setcors cors.xml s3://firstvdsТеперь, если обновить страницу S3 Manager, то увидим наши настройки, что подтверждает их сквозную конфигурацию между клиентами:

На примере через Windows клиент S3 Browser
S3 Browser — это бесплатное Windows-приложение с графическим интерфейсом для работы с файлами в S3. Программа предоставляет возможности для просмотра, загрузки, скачивания, шифрования и синхронизации данных, а также настройки прав доступа к бакетам.
Посмотрим текущую конфигурацию CORS в S3 Browser для нашего бакета:

Из скрина видно, что наши настройки сохранились.
Таким образом, CORS-настройками для S3-хранилища можно управлять разными способами. При этом все они работают с одной и той же конфигурацией бакета.
Итак, мы подробно рассмотрели настройку через S3 Manager FirstVDS, а также показали, как уже заданные правила выглядят при просмотре через CLI (s3cmd) и графический клиент S3 Browser.
Независимо от используемого инструмента, правила CORS применяются одинаково и дают полный контроль над тем, каким источникам и каким образом разрешён доступ к S3-ресурсам.
Для более безопасной настройки доступа можно совместить CORS и настройки политик безопасности бакета.
Небольшой трюк:
Чтобы не давать публичный доступ на чтение к бакету, мы можем его ограничить, например, по referer через политики:
:~$ s3cmd setpolicy cors.json s3://firstvds
s3://firstvds/: Policy updatedПолитика бакета:
:~$ cat cors.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadFromBucketWithReferer",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::firstvds/*",
"Condition": {
"StringLike": {
"aws:Referer": "https://example.com/*"
}
}
}
]
}
:~$ s3cmd setpolicy cors.json s3://firstvds
s3://firstvds/: Policy updatedЗапрос с указанием Referer — получаем доступ.
:~$ curl -X GET -s -I -H 'Referer: https://example.com/'
https://firstvds.s3.firstvds.ru/logo.png | head -n1
HTTP/1.1 200 OKЗапрос без указания Referer - доступ запрещен
:~$ curl -X GET -s -I https://firstvds.s3.firstvds.ru/logo.png | head -n1
HTTP/1.1 403 Forbidden
:~$ В итоге если вы используете Referer, то получаете следующее:
- CORS — пропускает междоменный запрос в браузере.
- Политика S3 — решает, кому вообще можно получить файл.
- В совокупности эти настройки позволяют:
- ограничить публичный доступ к бакету простой схемой;
- избежать блокировки ответов браузером по CORS для разрешённых origin;
- обеспечить загрузку файлов только с вашего сайта;
- получить код ответа 403 при обращении к объектам без заголовка Referer.
Но целиком полагаться на Referer не стоит, так он не дает 100%-ной защиты. Заголовок Referer может быть подменён или отсутствовать в некоторых сценариях.
Этот пример просто показывает сочетание нескольких настроек S3. Для более секьюрного подхода смотрите документацию по Signed URL и pre-signed URL.
Наш пример демонстрирует базовую, но часто встречающуюся настройку для публичных веб-сайтов, работающих с S3. Вы можете расширять конфигурацию под свои нужды, добавляя разные домены, методы или намного более строгие правила безопасности.
Распространённые проблемы при настройке CORS в S3 и как их исправить
Ниже разберём 5 самых частых проблем с CORS в S3 и способы их быстро устранить. В большинстве случаев вы увидите ошибку прямо в консоли браузера (DevTools), а для точной диагностики полезно сверить preflight/response в вкладке Network и при необходимости проверить доступ отдельно через curl -X GET — чтобы отличить CORS от реальной ошибки доступа (403 код ответа).
Проблема 1. Нет заголовка Access-Control-Allow-Origin
Симптомы. Вы получаете сообщение в DevTools консоли браузера или веб- приложении:
Access to fetch at 'https://...' from origin 'https://...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Возможные причины:
- CORS вообще не настроен для бакета.
- Указан неправильный домен (часто забывают
https://или указывают простоhttp://, в то время как запрос идет сhttps://). - Изменения не применяются (попали не в тот бакет, не сохранились).
Как исправить:
- Добавить точный домен в блок AllowedOrigins.
- Проверить, что используется правильный протокол (http ≠ https).
Обновить страницу в режиме инкогнито или очистить кэш.
Проблема 2. Используется * вместе с credentials (cookie, Authorization)
Симптомы. Вы получаете сообщение в DevTools консоли браузера или веб- приложении:
The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when the request's credentials mode is 'include'.
Возможные причины:
- В CORS указано "AllowedOrigins": ["*"].
- Запрос выполняется с credentials: include, или отправляются заголовки Authorization, Cookie.
Как исправить:
- Указать конкретный домен: "AllowedOrigins": ["https://example.com"]
Если доменов несколько - перечислить список, не использовать *.
Проблема 3. Не разрешены нужные методы или заголовки
Симптомы. Запросы PUT, POST, DELETE или с кастомными заголовками (Authorization, Content-Type) не проходят.
Вы получаете сообщение в DevTools консоли браузера или веб- приложении:
Response to preflight request doesn't pass access control check.
Возможные причины:
- В CORS нет нужных HTTP-методов.
- Не указаны AllowedHeaders.
Как исправить:
Указать верные методы через клиент или S3 Manager.
"AllowedMethods": ["GET", "PUT", "POST"], "AllowedHeaders": ["Authorization", "Content-Type", "*"]
Проблема 4. Старые настройки CORS сохраняются из-за кэша браузера
Симптомы:
- После изменения конфигурации всё равно возникает ошибка CORS.
- Вкладка Network → Response Headers показывает старые значения.
Возможные причины:
- Браузер кеширует preflight-ответы до Access-Control-Max-Age.
Как исправить:
- Очистить кэш или открыть страницу в режиме Инкогнито.
Уменьшить значение MaxAgeSeconds при тестировании (например, 30–60 сек).
Проблема 5. Код ответа 403 Forbidden , выдаваемая как CORS
Симптомы. Вы получаете сообщение в DevTools консоли браузера или веб- приложении:
CORS header 'Access-Control-Allow-Origin' missing Status code: 403 Forbidden
Фактическая причина в данном случае проблема связана не с CORS, а с отсутствием прав доступа к объекту. Заголовки CORS не добавляются, потому что S3-хранилище возвращает ошибку доступа:
- В политиках безопасности бакета запрещён доступ (Principal, Condition, aws:Referer).
- Бакет приватный из-за политики ACL, политик безопасности нет.
- Неверная подпись или expired signed URL, если настраивали ранее.
Как исправить:
- Проверить права в Политиках бакета, ACL.
- Убедиться, что объект существует и доступен.
- Тестировать через curl -X GET -I - если сразу 403, настройки CORS тут ни при чём.
Заключение
CORS — один из ключевых механизмов, который делает работу с объектным хранилищем S3 безопасной и предсказуемой. Благодаря ему веб-приложения и сайты могут получать данные из бакета, даже если находятся на другом домене, а браузер — не блокирует такие запросы.
Настройка CORS реализована гибко: правила можно задать через веб-интерфейс S3 Manager, с помощью CLI (s3cmd), другие десктопные клиенты. Это позволяет не только быстро внести изменения вручную, но и автоматизировать конфигурацию в рамках вашей инфраструктуры и архитектуры приложения. При необходимости правила легко изменить или удалить.
Правильно настроенный CORS:
- разрешает безопасные междоменные запросы к объектам;
- предотвращает ошибки браузера и блокировку контента;
- снижает нагрузку на сервер, так как файлы (обычно это статика) загружаются напрямую из S3.
Однако важно помнить: CORS не заменяет политики безопасности бакета. Он работает в браузере, а не управляет правами доступа. Поэтому максимальная защита достигается только при совместном использовании CORS, Политики безопасности бакета и ACL (не гибко).
Работа с CORS в S3 позволяет корректно и безопасно использовать файлы бакета на веб-сайтах, расположенных на других доменах. В статье были показаны основные принципы настройки CORS и практические примеры проверки конфигурации через S3 Manager, CLI и S3-клиенты.
Грамотно настроенный CORS устраняет ошибки в браузере и позволяет фронтенду напрямую обращаться к объектам в S3, не перегружая ваш сервер.