Add a packageSourceMapping section to your repository's nuget.config. List nuget.org with a star pattern, then list your private feed with the prefix your internal packages share. NuGet will then restore each package ID from the feed you assigned, instead of searching every source and taking whichever answers.
The problem it solves
By default, NuGet searches all configured package sources when it needs a package. Microsoft's documentation notes that when a package exists on several sources, it may not be deterministic which source it comes from. For a team with an internal package named like a public one, that is a dependency confusion setup. Source mapping lets you filter, per package, which sources NuGet may search.
It was added in NuGet 6.0, and older tooling ignores the configuration. Make sure every build machine uses compatible tooling, or the protection quietly does not apply.
A minimal configuration
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="internal" value="https://nuget.example.internal/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="internal">
<package pattern="Contoso.*" />
</packageSource>
</packageSourceMapping>
</configuration>
The clear element stops other config files from adding sources. Patterns are either an exact package ID or a prefix ending in a star. When several patterns match, the most specific wins, and the lone star has the lowest precedence. So Contoso packages go to the internal feed and everything else goes to nuget.org.
Steps to roll it out
- Put a single nuget.config at the repository root.
- Decide on a naming prefix for internal packages, and rename strays if you can.
- Declare a global packages folder for the repository. The docs note that if a package is already in the default global packages folder, no source lookup happens and the mappings are ignored.
- Restore, then list direct and transitive packages and confirm every ID matches a pattern. Once mapping is on, transitive packages must match too.
- Clear the global packages cache and restore again to validate.
Limits you should know
Mapping applies when NuGet downloads packages during restore, install and update. The docs say commands that query metadata, such as listing outdated or vulnerable packages, still send package IDs to all configured sources, including public ones. For internal package names that is an information leak to be aware of, even if it is not an install path.
If your internal packages have no shared prefix, the docs suggest a different shape: make the private source the default and map only what you want from public sources.
Where Safeguard fits
Safeguard's SBOM and dependency scanning can list the packages in a .NET project so you can check each ID against your mapping. A free scan is available at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen.
Open the inventory and look for any package whose name looks internal but whose source is not your private feed.
Quick check
Do I need source mapping if I only use nuget.org?
It matters most when you mix public and private sources. With one source there is nothing to confuse.
Why did restore fail after I added mappings?
Every package, including transitive ones, must match a pattern. Add the missing prefix or ID under the right source.
Sources
Last reviewed: 19 October 2026.