diff --git a/Duplicati/WebserverCore/Dto/V2/TestFilterRequestDto.cs b/Duplicati/WebserverCore/Dto/V2/TestFilterRequestDto.cs
new file mode 100644
index 000000000..df5b703d6
--- /dev/null
+++ b/Duplicati/WebserverCore/Dto/V2/TestFilterRequestDto.cs
@@ -0,0 +1,43 @@
+// Copyright (C) 2026, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+namespace Duplicati.WebserverCore.Dto.V2;
+
+///
+/// DTO for testing filesystem filters
+///
+public class TestFilterRequestDto
+{
+ ///
+ /// The paths to evaluate against the filters
+ ///
+ public string[]? Paths { get; set; }
+
+ ///
+ /// The source paths (roots) which are unconditionally included
+ ///
+ public string[]? Sources { get; set; }
+
+ ///
+ /// The filter strings to evaluate
+ ///
+ public string[]? Filters { get; set; }
+}
diff --git a/Duplicati/WebserverCore/Dto/V2/TestFilterResponseDto.cs b/Duplicati/WebserverCore/Dto/V2/TestFilterResponseDto.cs
new file mode 100644
index 000000000..803b7172f
--- /dev/null
+++ b/Duplicati/WebserverCore/Dto/V2/TestFilterResponseDto.cs
@@ -0,0 +1,78 @@
+// Copyright (C) 2026, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+namespace Duplicati.WebserverCore.Dto.V2;
+
+///
+/// DTO for the result of testing a single filesystem path against filters
+///
+public record TestFilterResponseItem
+{
+ ///
+ /// The path that was evaluated
+ ///
+ public string Path { get; set; } = "";
+
+ ///
+ /// Whether the path is included by the filters
+ ///
+ public bool Included { get; set; }
+
+ ///
+ /// The string representation of the filter that matched, if any
+ ///
+ public string? MatchedFilter { get; set; }
+}
+
+///
+/// DTO for the response of testing filesystem filters
+///
+public record TestFilterResponseDto : ResponseEnvelope
+{
+ ///
+ /// Creates a success response with the test results
+ ///
+ /// The data to include
+ /// The response DTO
+ public static TestFilterResponseDto Create(TestFilterResponseItem[] data)
+ => new TestFilterResponseDto()
+ {
+ Success = true,
+ Error = null,
+ StatusCode = "OK",
+ Data = data
+ };
+
+ ///
+ /// Creates an error response
+ ///
+ /// The error message
+ /// The status code
+ /// The response DTO
+ public static TestFilterResponseDto CreateError(string error, string statusCode)
+ => new TestFilterResponseDto()
+ {
+ Success = false,
+ Error = error,
+ StatusCode = statusCode,
+ Data = null
+ };
+}
diff --git a/Duplicati/WebserverCore/Endpoints/V2/TestFilters.cs b/Duplicati/WebserverCore/Endpoints/V2/TestFilters.cs
new file mode 100644
index 000000000..b7b951291
--- /dev/null
+++ b/Duplicati/WebserverCore/Endpoints/V2/TestFilters.cs
@@ -0,0 +1,102 @@
+// Copyright (C) 2026, The Duplicati Team
+// https://duplicati.com, hello@duplicati.com
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using Duplicati.Library.Utility;
+using Duplicati.Library.Common.IO;
+using Duplicati.WebserverCore.Abstractions;
+using Duplicati.WebserverCore.Dto.V2;
+using Microsoft.AspNetCore.Mvc;
+using Duplicati.Library.Interface;
+
+namespace Duplicati.WebserverCore.Endpoints.V2;
+
+public class TestFilters : IEndpointV2
+{
+ private static readonly string LOGTAG = Library.Logging.Log.LogTagFromType();
+
+ public static void Map(RouteGroupBuilder group)
+ {
+ group.MapPost("/filesystem/test-filter", ([FromBody] TestFilterRequestDto request) => Execute(request))
+ .RequireAuthorization();
+ }
+
+ private static TestFilterResponseDto Execute(TestFilterRequestDto request)
+ {
+ try
+ {
+ return TestFilterResponseDto.Create(Evaluate(request).ToArray());
+ }
+ catch (Exception ex)
+ {
+ Library.Logging.Log.WriteErrorMessage(LOGTAG, "TestFilters", ex, "An error occurred while testing filters");
+ return ex is UserInformationException uex
+ ? TestFilterResponseDto.CreateError(uex.Message, uex.HelpID)
+ : TestFilterResponseDto.CreateError(ex.Message, "InternalError");
+ }
+ }
+
+ private static IEnumerable Evaluate(TestFilterRequestDto request)
+ {
+ var filter = FilterExpression.Deserialize(request.Filters ?? Array.Empty());
+ var enumeratefilter = filter;
+
+ FilterExpression.AnalyzeFilters(filter, out var includes, out var excludes);
+ if (includes && !excludes)
+ enumeratefilter = FilterExpression.Combine(filter, new FilterExpression("*" + System.IO.Path.DirectorySeparatorChar, true));
+
+ var sourceArray = request.Sources ?? Array.Empty();
+ var sources = new HashSet(sourceArray, Utility.ClientFilenameStringComparer);
+
+ foreach (var path in request.Paths ?? Array.Empty())
+ {
+ if (path == null) continue;
+
+ // If the path is exactly one of the sources, it's always included.
+ if (sources.Contains(path))
+ {
+ yield return new TestFilterResponseItem
+ {
+ Path = path,
+ Included = true,
+ MatchedFilter = null
+ };
+ continue;
+ }
+
+ // Check if the path is under any of the source folders
+ var isUnderSource = sourceArray
+ .Any(source => string.Equals(Util.AppendDirSeparator(path), Util.AppendDirSeparator(source), Utility.ClientFilenameStringComparison) ||
+ Utility.IsPathBelowFolder(path, source));
+
+ // Do not return paths that are not part of the source selection
+ if (!isUnderSource)
+ continue;
+
+ var isIncluded = FilterExpression.Matches(enumeratefilter!, path, out var match);
+ yield return new TestFilterResponseItem
+ {
+ Path = path,
+ Included = isIncluded,
+ MatchedFilter = match?.ToString()
+ };
+ }
+ }
+}
diff --git a/Duplicati/WebserverCore/Services/SystemInfoProvider.cs b/Duplicati/WebserverCore/Services/SystemInfoProvider.cs
index 324203186..d98e99d09 100644
--- a/Duplicati/WebserverCore/Services/SystemInfoProvider.cs
+++ b/Duplicati/WebserverCore/Services/SystemInfoProvider.cs
@@ -59,6 +59,7 @@ public class SystemInfoProvider(IApplicationSettings applicationSettings, Connec
"v2:system:temp-disk-space",
"v1:subscribe:remotecontrol",
"v1:ipc:controller",
+ "v2:filesystem:test-filter"
// "v1:subscribe:scheduler",
];