AF.Umbraco.S3.Media.Storage 1.4.2

dotnet add package AF.Umbraco.S3.Media.Storage --version 1.4.2
                    
NuGet\Install-Package AF.Umbraco.S3.Media.Storage -Version 1.4.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AF.Umbraco.S3.Media.Storage" Version="1.4.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AF.Umbraco.S3.Media.Storage" Version="1.4.2" />
                    
Directory.Packages.props
<PackageReference Include="AF.Umbraco.S3.Media.Storage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AF.Umbraco.S3.Media.Storage --version 1.4.2
                    
#r "nuget: AF.Umbraco.S3.Media.Storage, 1.4.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AF.Umbraco.S3.Media.Storage@1.4.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AF.Umbraco.S3.Media.Storage&version=1.4.2
                    
Install as a Cake Addin
#tool nuget:?package=AF.Umbraco.S3.Media.Storage&version=1.4.2
                    
Install as a Cake Tool

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 IFileSystem for Media.
  • Middleware for direct media delivery from S3 under /media.
  • ImageSharp integration for dynamic thumbnails.
  • Mirrored S3 cache for supported image files 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 and the ImageSharp query-routing fix in PR #9.
  • ElBart00 for reporting the asynchronous S3 exception-translation issue in issue #10.
  • 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.2
  • Umbraco CMS: 15.x, 16.x, 17.x, 18.x
  • .NET: 9.0, 10.0 (Umbraco 18.x requires 10.0)
  • AWS SDK for .NET: AWSSDK.S3 + AWSSDK.Extensions.NETCore.Setup

Current Release

1.4.2 improves S3 media delivery and ImageSharp integration across Umbraco 15.x-18.x:

Missing S3 objects now follow the filesystem and HTTP not-found paths instead of surfacing asynchronous AWS failures as AggregateException and HTTP 500. Synchronous IFileSystem operations observe AWS task failures inside the existing exception-mapping boundary, including reads, metadata, listing, uploads, and deletes.

The ImageSharp source provider now checks the requested S3 object rather than the bucket. A missing key is passed to the remaining middleware pipeline for HTTP 404 handling, while authorization failures remain visible. Successful metadata is reused by the resolver, avoiding the previous bucket check and duplicate metadata requests.

The package composer now runs explicitly after Umbraco's ImageSharp composer and inserts the S3 provider ahead of the default web-root provider. This prevents transformed media requests such as ?width=200 from returning HTTP 404 after Umbraco resets its provider collection during startup.

Original media is now delivered from S3 when the URL contains only query parameters unrelated to ImageSharp, such as Umbraco's v parameter. Recognized commands, including commands supplied by custom processors, continue to be routed to ImageSharp when combined with unrelated parameters. The behavior remains consistent for dependency-injection activation and legacy constructors.

The ImageSharpMiddlewareOptions.OnParseCommandsAsync callback remains exclusively managed by the ImageSharp middleware, avoiding duplicate invocations and related side effects. URLs that rely on commands created only by this callback must include at least one registered command to be routed to ImageSharp.

Thanks to koty10 for the query-routing contribution in PR #9, and to ElBart00 for reporting the asynchronous S3 failure mode in issue #10.

Previous Releases

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, and src/Umbraco.Cms.18.x.
  • Each host supports local overrides through appsettings.Local.json.
  • VS Code Run and Debug configurations and their build tasks are available for every compatibility host.
Host HTTP HTTPS
Umbraco 15 http://localhost:5015 https://localhost:44375
Umbraco 16 http://localhost:5016 https://localhost:44376
Umbraco 17 http://localhost:5017 https://localhost:44377
Umbraco 18 http://localhost:5018 https://localhost:44378

Umbraco 18 uses Umbraco:CMS:Imaging:HMACSecretKey. The tracked appsettings.json contains an empty placeholder; set the real value only in the git-ignored 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

Create the NuGet package:

dotnet pack src/AF.Umbraco.S3.Media.Storage/AF.Umbraco.S3.Media.Storage.csproj -c Release

The suite includes regression coverage for S3 exception translation, awaited deletes, ImageSharp composer/provider ordering, missing-object 404 handling, metadata reuse, authorization failures, query routing, legacy constructors, complete S3 delivery, custom processors, SVG uploads, and rejection of invalid PNG content.

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:Media is package/provider configuration (BucketName, retention, media behavior).
  • AWS is AWS SDK client configuration (Profile, ServiceURL, ForcePathStyle, default Region).

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:

  1. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY (for example set only in local launchSettings.json)
  2. ~/.aws/credentials using the profile configured in AWS:Profile (for example YOUR_AWS_PROFILE)
  3. 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 = true overrides Enabled.
  • 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/health
  • POST /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 (supported images only): cache/... by default, or {CacheBucketPrefix}/... when configured.
  • ImageSharp transformed cache: cache/... by default, or {CacheBucketPrefix}/... when configured.
  • For ease of management, cached images replicate the corresponding media folder hierarchy under the cache prefix.

Localization for validation errors

The package includes localized messages for:

  • it-IT
  • en-US (default fallback)
  • fr-FR
  • es-ES
  • de-DE
  • da-DK

If a specific culture resource is missing, the package falls back to en-US.

Project documentation

For full technical documentation:

  • docs/README.md
  • docs/API_REFERENCE.md
  • docs/ARCHITECTURE.md
  • docs/CONFIGURATION.md
  • docs/DEVELOPMENT.md
  • docs/TESTING.md
  • docs/MAINTENANCE.md
  • docs/PROJECT_STRUCTURE.md
  • docs/CHANGELOG.md
  • docs/manuals/technical-manual.md
  • docs/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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.2 88 9/8/2026
1.4.1 259 8/22/2026
1.3.0 2,657 4/28/2026
1.2.1 2,574 2/10/2026
1.2.0 137 2/8/2026
1.1.0 131 2/5/2026
1.0.0 170 2/5/2026

1.4.2: Fixed original S3 media delivery when URLs contain unrelated query parameters such as Umbraco's cache-busting v parameter. Fixed asynchronous AWS SDK exception translation and missing ImageSharp source handling so absent media follows not-found paths instead of returning HTTP 500. Corrected ImageSharp composer/provider ordering, reused S3 metadata, and expanded .NET 9/.NET 10 regression coverage. Thanks to koty10 for PR #9 and ElBart00 for reporting issue #10.