Своё хранилище + CDN
Подключите собственное объектное хранилище и собственный CDN на тарифах API Velocity и Momentum: настройка для каждого провайдера, минимальные права, проверка подключения и что происходит при смене режима.
Доступно на: тарифах API Velocity и Momentum. Недоступно на Ignite (в нём нет CDN), в песочнице для разработчиков (файлы песочницы временные) и на тарифах WordPress (они используют управляемый CDN smallPict).
Кратко: в режиме BYO smallPict записывает ваши оригиналы и оптимизированные изображения в ваш бакет, а отдаёт их ваш CDN. Хранилище и CDN всегда подключаются вместе: нельзя подключить одно без другого.
Что такое BYO
На Velocity и Momentum есть два режима доставки. Режим выбирается в разделе Панель управления → CDN и хранилище.
- Управляемый (по умолчанию): smallPict хранит ваши оригиналы в своём зашифрованном хранилище (через 90 дней они переносятся в долгосрочный архив) и отдаёт оптимизированные изображения с
cdn.smallpict.app. Ничего настраивать не нужно. - Своё хранилище + CDN (BYO): вы подключаете S3-совместимый бакет в своём аккаунте и CDN в своём аккаунте. Каждое новое задание записывает оригинал и оптимизированный файл в ваш бакет, а API возвращает URL на домене вашего CDN.
Правило «только вместе» существует потому, что smallPict должен выполнять обе части доставки: записывать файлы и удалять устаревшие копии из кеша. Бакету без CDN некуда было бы отправлять очистку кеша; CDN без бакета было бы нечего отдавать.
Управляемый режим и BYO
| Управляемый (по умолчанию) | Своё хранилище + CDN | |
|---|---|---|
| Оригиналы | Зашифрованное хранилище smallPict | originals/<job_id>/<file> в вашем бакете |
| Оптимизированные файлы | Отдаются с cdn.smallpict.app | optimized/<job_id>.<ext> в вашем бакете, отдаются с домена вашего CDN |
| Квота хранилища | Velocity 50 ГБ, Momentum 100 ГБ | Не учитывается. Вы платите своему провайдеру хранилища. |
| Трафик CDN | Velocity 30 ГБ/месяц, Momentum 200 ГБ/месяц | Не учитывается. Вы платите своему провайдеру CDN. |
| Преобразования | Учитываются в тарифе | Учитываются в тарифе |
| Копия у smallPict | Да, пока ваш аккаунт активен | Нет. Только временная загрузка для обработки, удаляется в течение 24 часов. |
| Очистка кеша | Автоматически | Автоматически, через API вашего CDN |
| Производительность, домен и стоимость CDN | smallPict | Вы. smallPict не отвечает за производительность, домены и счета вашего CDN. |
Поддерживаемые провайдеры
Хранилище (доступно сейчас): Amazon S3, Cloudflare R2, Google Cloud Storage (совместимость с S3), Alibaba Cloud OSS, Tencent Cloud COS, DigitalOcean Spaces, SumoPod Storage, MinIO и любой другой S3-совместимый сервис через собственный эндпоинт (например, Hetzner, Vultr или Wasabi).
CDN (доступно сейчас): Cloudflare и Amazon CloudFront.
Запланировано: Azure Blob Storage и другие CDN. Запросить ранний доступ можно на странице Облачные провайдеры.
Перед началом
- Вы на тарифе API Velocity или Momentum и вошли как администратор аккаунта.
- У вас есть бакет и CDN, который отдаёт файлы из этого бакета по HTTPS.
- Эндпоинт хранилища доступен из интернета по HTTPS. Частные, внутренние и HTTP-эндпоинты без шифрования отклоняются.
- Вы создали для smallPict ключи с узкими правами (см. Минимальные права ниже). Не используйте root- или админ-ключи своего аккаунта.
Затем откройте Панель управления → CDN и хранилище, выберите Своё хранилище + CDN, заполните обе части и нажмите Сохранить хранилище + CDN. smallPict выполнит проверку подключения (описана ниже) и переключит вас на BYO, только если она пройдена.
Руководства по настройке
A. Amazon S3 + Amazon CloudFront
- Создайте бакет в нужном регионе, например
my-imagesвap-southeast-1. Оставьте Block Public Access включённым. - Создайте дистрибутив CloudFront с бакетом в качестве источника. Используйте Origin access control (OAC), чтобы CloudFront мог читать бакет, пока тот остаётся приватным, и примените политику бакета, которую предложит CloudFront.
- Необязательно: свой домен. Добавьте в дистрибутив альтернативное доменное имя (например,
images.example.com) и сертификат, а затем направьте DNS-запись на дистрибутив. - Создайте пользователя (или роль) IAM для хранилища с такой политикой:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::my-images/*" } ]}- Создайте пользователя IAM для CloudFront (это может быть тот же пользователь) с такой политикой.
cloudfront:GetDistributionнеобязательно; с этим правом smallPict может определить домен дистрибутива, если вы оставите домен CDN пустым.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "cloudfront:CreateInvalidation", "cloudfront:GetDistribution" ], "Resource": "arn:aws:cloudfront::123456789012:distribution/E2QWRUHAPOMQZL" } ]}- В панели управления:
- Провайдер хранилища Amazon S3, бакет
my-images, регионap-southeast-1, эндпоинт пустой (используется стандартный эндпоинт региона), а также access key ID и секрет для хранилища. - CDN Amazon CloudFront, Distribution ID (например,
E2QWRUHAPOMQZL), access key ID и секрет для CloudFront и, при желании, домен CDN (https://images.example.com). Оставьте домен пустым, чтобы использовать собственный домен дистрибутива*.cloudfront.net.
- Провайдер хранилища Amazon S3, бакет
B. Cloudflare R2 + Cloudflare
- Создайте бакет R2, например
my-images. - Подключите к бакету собственный домен (R2 → ваш бакет → Settings → Custom Domains), например
images.example.com, в зоне того же аккаунта Cloudflare. Запросы к этому домену проходят через кеш Cloudflare. Не используйте URL разработкиr2.devв продакшене. - Создайте API-токен R2 (R2 → Manage API tokens) с правом Object Read & Write, ограниченный только этим бакетом. Скопируйте показанные Access Key ID и Secret Access Key.
- Создайте API-токен Cloudflare (My Profile → API Tokens) для зоны с правом Zone → Cache Purge → Purge. Zone → Zone → Read — необязательно.
- В панели управления:
- Провайдер хранилища Cloudflare R2, бакет
my-images, эндпоинтhttps://<account_id>.r2.cloudflarestorage.com(указан в параметрах S3 API вашего бакета), регионauto, а также Access Key ID и Secret Access Key от R2. - CDN Cloudflare, Zone ID (страница Overview домена, раздел API), API-токен и домен CDN
https://images.example.com.
- Провайдер хранилища Cloudflare R2, бакет
C. Google Cloud Storage + Cloudflare
Google Cloud Storage подключается через свой S3-совместимый XML API с ключами HMAC.
- Создайте бакет, например
my-images. - Создайте сервисный аккаунт и выдайте ему роль Storage Object User (
roles/storage.objectUser) только на этот бакет. - Создайте ключ HMAC для сервисного аккаунта: Cloud Storage → Settings → Interoperability → Create a key for a service account. Скопируйте access ID и секрет.
- Сделайте оптимизированные файлы доступными для чтения вашему CDN. Cloudflare получает файлы из Cloud Storage по HTTPS, поэтому объекты в
optimized/должны быть публично читаемыми. При единообразном доступе на уровне бакета (uniform bucket-level access) выдачаallUsersроли Storage Object Viewer делает читаемым весь бакет, включаяoriginals/. Если оригиналы должны оставаться приватными, поставьте перед бакетом слой аутентификации (например, Cloudflare Worker, который подписывает запросы к бакету). - Направьте проксируемую DNS-запись Cloudflare на Cloud Storage, например
images.example.com. Либо назовите бакет по имени хоста и используйте проксируемую CNAME-запись наc.storage.googleapis.com, либо используйте Cloudflare Origin Rule, которое отправляет запросы наstorage.googleapis.comс именем бакета в начале пути. - Создайте API-токен Cloudflare для зоны с правом Zone → Cache Purge → Purge (Zone → Zone → Read — необязательно).
- В панели управления: провайдер хранилища Google Cloud Storage, бакет, эндпоинт
https://storage.googleapis.com, регионauto, access ID и секрет HMAC; CDN Cloudflare, Zone ID, API-токен и домен CDNhttps://images.example.com.
D. MinIO или любое S3-совместимое хранилище + Cloudflare
Этот раздел охватывает MinIO и S3-совместимые сервисы, такие как Hetzner, Wasabi, Vultr, SumoPod, Alibaba Cloud OSS, Tencent Cloud COS и DigitalOcean Spaces.
| Провайдер | Что выбрать в панели | Эндпоинт | Примечания |
|---|---|---|---|
| MinIO | MinIO | Ваш сервер, например https://minio.example.com | Path-style запросы включены. Сервер должен быть доступен из интернета по HTTPS. |
| Hetzner Object Storage | Другое S3-совместимое | https://<location>.your-objectstorage.com | |
| Wasabi | Другое S3-совместимое | https://s3.<region>.wasabisys.com | |
| Vultr Object Storage | Другое S3-совместимое | https://<region>.vultrobjects.com | |
| SumoPod Storage | SumoPod Storage | Из панели хранилища SumoPod | Path-style включён по умолчанию. |
| Alibaba Cloud OSS | Alibaba Cloud OSS | https://oss-<region>.aliyuncs.com | Только стиль virtual-hosted. |
| Tencent Cloud COS | Tencent Cloud COS | https://cos.<region>.myqcloud.com | Имя бакета включает ваш APPID, например my-images-1250000000. |
| DigitalOcean Spaces | DigitalOcean Spaces | https://<region>.digitaloceanspaces.com |
- Создайте бакет и ключ, ограниченный этим бакетом, с правами на запись, чтение и удаление объектов (см. Минимальные права ниже).
- Сделайте
optimized/доступным для чтения вашему CDN, например политикой бакета, которая разрешает анонимныйs3:GetObjectтолько дляoptimized/*. В MinIO:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "*" ] }, "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::my-images/optimized/*" ] } ]}- Поставьте Cloudflare перед бакетом: проксируемая DNS-запись для
images.example.com, указывающая на публичный хост бакета. Если провайдеру нужен его собственный хост в запросе, добавьте Cloudflare Origin Rule, которое переписывает заголовок Host (а для path-style хостов добавляет имя бакета в путь). - Создайте API-токен Cloudflare для зоны с правом Zone → Cache Purge → Purge (Zone → Zone → Read — необязательно).
- В панели управления: выберите провайдера из таблицы, укажите бакет, эндпоинт, регион (
auto, если у провайдера его нет) и ключи; включите path-style запросы там, где это указано в таблице; затем CDN Cloudflare, Zone ID, API-токен и домен CDN.
Минимальные права
Выдайте smallPict ключи, которые могут делать только необходимое и только в одном бакете.
| Провайдер | Права |
|---|---|
| Amazon S3 | s3:PutObject, s3:GetObject, s3:DeleteObject на arn:aws:s3:::<bucket>/* |
| Amazon CloudFront | cloudfront:CreateInvalidation на дистрибутив; необязательно cloudfront:GetDistribution, чтобы домен определялся автоматически |
| Cloudflare R2 | API-токен R2 с правом Object Read & Write, ограниченный бакетом |
| Cloudflare (CDN) | API-токен для зоны с правом Zone → Cache Purge → Purge; необязательно Zone → Zone → Read |
| Google Cloud Storage | Ключ HMAC для сервисного аккаунта с roles/storage.objectUser на бакет |
| Alibaba Cloud OSS | Пользователь RAM с oss:PutObject, oss:GetObject, oss:DeleteObject на бакет |
| Tencent Cloud COS | Субпользователь CAM с cos:PutObject, cos:GetObject, cos:DeleteObject на бакет (имя бакета включает APPID) |
| DigitalOcean Spaces | Ключ доступа Spaces, ограниченный бакетом, с правами на чтение, запись и удаление |
| MinIO / другое S3-совместимое | Запись, чтение и удаление для <bucket>/*; эндпоинт должен быть публичным HTTPS |
Проверка подключения
При каждом сохранении (и при нажатии Повторить проверку) smallPict проверяет всё подключение, прежде чем его использовать:
- Хранилище: записывает небольшой тестовый файл в
.smallpict-probe/в вашем бакете, считывает его и удаляет. - Cloudflare: отправляет в вашу зону одну тестовую очистку кеша для одного URL.
- CloudFront: создаёт одну тестовую инвалидацию для пути
/.smallpict-probe/.... Она учитывается в месячном количестве путей инвалидации CloudFront.
Если какой-либо шаг не пройден, ничего не переключается. Панель показывает причину рядом с соответствующим полем, например у имени бакета или API-токена. Проверка подключения ограничена 5 попытками в минуту.
Доступ CDN к бакету
optimized/должен быть доступен вашему CDN для чтения: либо публичное чтение для этого префикса, либо доступ CDN к приватному бакету через источник (origin access control в CloudFront или собственный домен R2).originals/может оставаться приватным. Вашему CDN он никогда не нужен.- CORS нужен, только если браузеры загружают изображения с другого источника из JavaScript (например, через
fetch()или canvas). Обычным тегам<img>он не нужен. Если он всё же нужен, разрешитеGETиHEADс источника вашего сайта.
Структура объектов и URL
| Что | Ключ в вашем бакете | URL |
|---|---|---|
| Оригинал | originals/<job_id>/<file> | Не отдаётся |
| Оптимизированный файл | optimized/<job_id>.<ext> | <cdn_domain>/optimized/<job_id>.<ext> |
Домен CDN может содержать префикс пути, например https://example.com/images; тогда URL будет https://example.com/images/optimized/<job_id>.<ext>.
Заголовки кеширования
Оптимизированные файлы записываются с заголовком Cache-Control: public, max-age=31536000, immutable. Каждое задание получает уникальный ключ, поэтому новый результат всегда имеет новый URL и очистка кеша нужна редко. Следите, чтобы правила кеширования вашего CDN не заменяли этот заголовок более коротким сроком.
Очистка кеша
- Запросы на очистку через API (
POST /v1/purge) отправляются в ваш CDN пакетами. - В Cloudflare файлы очищаются по URL. «Очистить всё» очищает только ваш хост доставки и префикс пути, а не всю зону.
- В CloudFront файлы очищаются инвалидациями. «Очистить всё» создаёт инвалидацию
/*. Пути инвалидации сверх месячного бесплатного лимита CloudFront AWS выставляет в счёт вашему аккаунту.
Смена режима
Смена режима не перемещает существующие файлы. Уже доставленные файлы сохраняют свои текущие URL; новый режим применяется только к новым заданиям.
- Управляемый → BYO: новые задания записываются в ваш бакет и отдаются вашим CDN. Файлы, уже находящиеся на
cdn.smallpict.app, остаются там. - BYO → управляемый: новые задания снова используют управляемое хранилище и CDN. Ваши настройки и ключи BYO остаются сохранёнными, но не используются, поэтому вы можете переключиться обратно без повторного ввода, пока не нажмёте Отключить.
- Отключить: удаляет сохранённые ключи и настройки BYO и возвращает вас в управляемый режим. Файлы в вашем бакете не затрагиваются.
Если что-то пошло не так
- Если smallPict не может записать файл в ваш бакет, он повторяет попытки, а затем завершает задание с ошибкой и указывает причину в ответе API.
- Панель показывает последнюю ошибку и время, когда она произошла, вместе с кнопкой Повторить проверку.
- Вы получаете одно письмо на инцидент, а не на каждое неудачное задание.
- smallPict никогда не переключается на управляемое хранилище автоматически. Ваши файлы никогда не хранятся там, где вы этого не выбирали.
Безопасность
- Ключи хранятся в зашифрованном хранилище секретов. Они больше никогда не показываются, не возвращаются через API и не записываются в логи.
- Чтобы заменить ключ, введите новый и сохраните; проверка подключения выполнится с новым ключом. Оставьте поле ключа пустым, чтобы сохранить текущий ключ.
- Эндпоинты хранилища и CDN должны использовать HTTPS. Эндпоинты, которые разрешаются в частные, loopback, link-local или другие внутренние адреса, отклоняются.
- Используйте для smallPict отдельные ключи с узкими правами и отзовите их в консоли провайдера, если перестанете пользоваться BYO.