AF.Umbraco.S3.Media.Storage
1.4.1
dotnet add package AF.Umbraco.S3.Media.Storage --version 1.4.1
NuGet\Install-Package AF.Umbraco.S3.Media.Storage -Version 1.4.1
<PackageReference Include="AF.Umbraco.S3.Media.Storage" Version="1.4.1" />
<PackageVersion Include="AF.Umbraco.S3.Media.Storage" Version="1.4.1" />
<PackageReference Include="AF.Umbraco.S3.Media.Storage" />
paket add AF.Umbraco.S3.Media.Storage --version 1.4.1
#r "nuget: AF.Umbraco.S3.Media.Storage, 1.4.1"
#:package AF.Umbraco.S3.Media.Storage@1.4.1
#addin nuget:?package=AF.Umbraco.S3.Media.Storage&version=1.4.1
#tool nuget:?package=AF.Umbraco.S3.Media.Storage&version=1.4.1
AF.Umbraco.S3.Media.Storage
AWS S3 media storage provider for Umbraco 15/16/17/18 on .NET 9/10.
This package replaces the default media file system with an S3-backed implementation and includes:
- S3-backed implementation of Umbraco
IFileSystemfor Media. - Middleware for direct media delivery from S3 under
/media. - ImageSharp integration for dynamic thumbnails.
- S3 cache for all media files (
cache/mirror) plus ImageSharp transformed images. - Localized server-side validation for malformed image uploads.
- Startup S3 connectivity check that blocks app boot on connection failure.
- Optional package-hosted smoke endpoints (opt-in via
AF_SMOKE_TESTS=1). - Optional cache-retention cleanup with normal and test modes.
- Cache folder structure that mirrors the media folder structure.
Credits
This project is a porting of Our.Umbraco.StorageProviders.AWSS3
(adam-werner/Our.Umbraco.StorageProviders.AWSS3),
which is not compatible with recent Umbraco versions.
AF.Umbraco.S3.Media.Storage was fully refactored to be compatible with modern Umbraco versions and then further optimized and extended.
Thanks to community contributors:
- koty10 for the SVG upload-validation fix in PR #3.
- proxicode for the configurable bucket-prefix contribution and related integration fixes in PR #4.
- suedeapple for Umbraco 18 compatibility and the ImageSharp request-handling and S3 cache integration fixes in PR #7.
Compatibility
- Current package version:
1.4.1 - Umbraco CMS:
15.x,16.x,17.x,18.x - .NET:
9.0,10.0(Umbraco 18.x requires10.0) - AWS SDK for .NET:
AWSSDK.S3+AWSSDK.Extensions.NETCore.Setup
Current Release
1.4.1 updates contributor credits in the README and Umbraco Marketplace metadata.
1.4.0 adds Umbraco 18.x compatibility and updates AWSS3Composer for Umbraco 18's changed UmbracoPipelineFilter constructor, continuing compatibility across Umbraco 15.x-18.x. It also ensures ImageSharp resize requests are processed before S3 media delivery and stores transformed images in the configured S3 cache rather than the local default cache.
1.3.0 adds configurable S3 MediaBucketPrefix and CacheBucketPrefix support, keeps local public media URLs on Umbraco's media path unless BucketHostName is configured, normalizes configured prefixes, and adds regression tests/documentation for the new behavior.
Thanks to proxicode for the configurable bucket-prefix contribution and related integration fixes in PR #4, and to koty10 for the SVG upload-validation fix in PR #3.
Test hosts and smoke CI
- Local compatibility hosts are included under
src/Umbraco.Cms.15.x,src/Umbraco.Cms.16.x,src/Umbraco.Cms.17.x, andsrc/Umbraco.Cms.18.x. - Each host supports local overrides through
appsettings.Local.json.
Build and test
Build the package:
dotnet build src/AF.Umbraco.S3.Media.Storage/AF.Umbraco.S3.Media.Storage.csproj --no-restore
Run unit tests:
dotnet test src/AF.Umbraco.S3.Media.Storage.Tests/AF.Umbraco.S3.Media.Storage.Tests.csproj
The unit test suite includes a regression for SVG uploads being accepted and invalid PNG content still being rejected by ImageSharp validation.
Installation
Install from NuGet:
dotnet add package AF.Umbraco.S3.Media.Storage
Basic setup
1) No Program.cs changes required
The package wires services and middleware automatically via a composer.
You only need to configure appsettings*.json.
2) Configure appsettings*.json
Minimal setup:
{
"Umbraco": {
"Storage": {
"AWSS3": {
"Media": {
"BucketName": "your-media-bucket",
"Region": "eu-west-1"
}
}
}
}
}
Optional S3 object prefixes:
{
"Umbraco": {
"Storage": {
"AWSS3": {
"Media": {
"BucketName": "your-media-bucket",
"Region": "eu-west-1",
"MediaBucketPrefix": "tenant-a/media",
"CacheBucketPrefix": "tenant-a/cache"
}
}
}
}
}
MediaBucketPrefix controls the internal S3 object key prefix for original media files. CacheBucketPrefix controls mirrored media cache and ImageSharp transformed cache keys. Public media URLs still use Umbraco's configured media path, normally /media, unless BucketHostName is configured for CDN/S3 public URLs.
For public/open-source repositories, keep placeholders in appsettings.Development.json and store real local values in appsettings.Local.json (git-ignored).
For standard AWS endpoints, configure only the region and credentials; do not set ServiceURL:
{
"AWS": {
"Profile": "YOUR_AWS_PROFILE",
"Region": "eu-west-1"
}
}
Use ServiceURL only for a non-standard S3-compatible endpoint such as MinIO, and keep it in the git-ignored appsettings.Local.json:
{
"AWS": {
"Profile": "YOUR_AWS_PROFILE",
"Region": "eu-west-1",
"ServiceURL": "http://localhost:9000",
"ForcePathStyle": true
}
}
Why two sections:
Umbraco:Storage:AWSS3:Mediais package/provider configuration (BucketName, retention, media behavior).AWSis AWS SDK client configuration (Profile,ServiceURL,ForcePathStyle, defaultRegion).
In short: Storage defines what the provider does, AWS defines how the SDK connects. ServiceURL overrides the normal regional AWS endpoint resolution, so it must never contain a placeholder value in a configuration file that can be loaded at runtime.
3) AWS secrets for local development
Keep AWS secrets out of the repository.
Use local shared credentials on your machine (for example ~/.aws/credentials):
[YOUR_AWS_PROFILE]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
Credential precedence used by this project:
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY(for example set only in locallaunchSettings.json)~/.aws/credentialsusing the profile configured inAWS:Profile(for exampleYOUR_AWS_PROFILE)- Other standard AWS SDK providers (for example IAM role on cloud hosts)
Cache retention
Thumbnail cache retention is configurable via:
{
"Umbraco": {
"Storage": {
"AWSS3": {
"Media": {
"CacheRetention": {
"Enabled": false,
"NumberOfDays": 90,
"TestModeEnable": false,
"TestModeSweepSeconds": 30,
"TestModeMaxAgeMinutes": 10
}
}
}
}
}
}
Rules:
TestModeEnable = trueoverridesEnabled.- In normal mode, cleanup uses
NumberOfDays. - In test mode, cleanup sweep/max-age are controlled in seconds/minutes.
Setup validation
- When Umbraco starts, the package checks AWS connectivity.
- If AWS connectivity is invalid, startup may be blocked by the package startup validation.
Smoke endpoints (opt-in)
For local validation and CI checks you can enable built-in smoke endpoints by setting:
AF_SMOKE_TESTS=1
Endpoints:
GET /smoke/healthPOST /smoke/media-upload
These endpoints are disabled by default.
Logging and alerts
- Package logs are emitted in English and include the
[AFUS3MS]prefix for easy filtering. - Startup connectivity failures are logged as critical and block Umbraco startup.
- Upload/cache/delete storage failures are logged with the same prefix and returned to users with localized alert messages.
S3 object layout
- Original media files:
media/...by default, or{MediaBucketPrefix}/...when configured. - Mirrored media cache (all media types):
cache/...by default, or{CacheBucketPrefix}/...when configured. - ImageSharp transformed cache:
cache/...by default, or{CacheBucketPrefix}/...when configured. - For ease of management, the
cachefolder replicates themediafolder hierarchy, ensuring a one-to-one correspondence between each media folder and its cache folder.
Localization for validation errors
The package includes localized messages for:
it-ITen-US(default fallback)fr-FRes-ESde-DEda-DK
If a specific culture resource is missing, the package falls back to en-US.
Project documentation
For full technical documentation:
docs/README.mddocs/API_REFERENCE.mddocs/ARCHITECTURE.mddocs/CONFIGURATION.mddocs/DEVELOPMENT.mddocs/TESTING.mddocs/MAINTENANCE.mddocs/PROJECT_STRUCTURE.mddocs/CHANGELOG.mddocs/manuals/technical-manual.mddocs/manuals/user-manual.md
Security checks
This repository runs automated secret scanning in GitHub Actions via Gitleaks (.github/workflows/secret-scan.yml).
Security advisory notice
This package does not introduce the known Umbraco advisory GHSA-69cg-w8vm-h229, but it can be installed on Umbraco versions that may still include it.
For production usage, always install the latest patched Umbraco release in your major/minor line.
Attribution request (non-binding)
If you fork or modify this project, please consider adding credits to:
- Project:
AF.Umbraco.S3.Media.Storage - Author:
Adriano Fabri - Url:
https://github.com/afabri73/AF.Umbraco.S3.Media.Storage
License
This project is licensed under MIT. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- AWSSDK.Extensions.NETCore.Setup (>= 4.0.3.22)
- AWSSDK.S3 (>= 4.0.101.4)
- SixLabors.ImageSharp.Web.Providers.AWS (>= 3.2.0)
- Umbraco.Cms.Web.Common (>= 15.0.0 && < 19.0.0)
-
net9.0
- AWSSDK.Extensions.NETCore.Setup (>= 4.0.3.22)
- AWSSDK.S3 (>= 4.0.101.4)
- SixLabors.ImageSharp.Web.Providers.AWS (>= 3.2.0)
- Umbraco.Cms.Web.Common (>= 15.0.0 && < 19.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.4.1: Updated README and Umbraco Marketplace contributor credits to acknowledge suedeapple for the Umbraco 18 compatibility, ImageSharp request-handling, and S3 cache integration fixes contributed in PR #7. 1.4.0: Added Umbraco 18.x compatibility by widening the Umbraco.Cms.Web.Common version range to [15.0.0,19.0.0) and adding a src/Umbraco.Cms.18.x local test host. Fixed a runtime MissingMethodException on UmbracoPipelineFilter under Umbraco 18 by switching the composer to the version-stable single-argument constructor plus property initializers. Fixed ImageSharp resize requests being served as original files and ensured transformed images use the package S3 cache instead of Umbraco's local default cache. Removed unsafe AWS ServiceURL placeholders from development samples and updated AWSSDK.S3. 1.3.0: Added configurable MediaBucketPrefix and CacheBucketPrefix support with normalized S3 keys, corrected public GetUrl behavior so local media URLs stay on the Umbraco media path unless BucketHostName is configured, added configurable cache-prefix handling, and expanded regression tests/documentation.