diff --git a/src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs b/src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs
index 3a0dca61219..29924eae74b 100644
--- a/src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs
+++ b/src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs
@@ -1,10 +1,6 @@
 // Copyright The OpenTelemetry Authors
 // SPDX-License-Identifier: Apache-2.0
 
-#if NET
-using System.Diagnostics.CodeAnalysis;
-#endif
-using System.Net;
 using System.Text;
 using OpenTelemetry.Internal;
 
@@ -20,9 +16,6 @@ public class BaggagePropagator : TextMapPropagator
     private const int MaxBaggageLength = 8192;
     private const int MaxBaggageItems = 180;
 
-    private static readonly char[] EqualSignSeparator = ['='];
-    private static readonly char[] CommaSignSeparator = [','];
-
     /// 
     public override ISet Fields => new HashSet { BaggageHeaderName };
 
@@ -52,7 +45,7 @@ public override PropagationContext Extract(PropagationContext context, T carr
             var baggageCollection = getter(carrier, BaggageHeaderName);
             if (baggageCollection?.Any() ?? false)
             {
-                if (TryExtractBaggage([.. baggageCollection], out var baggage))
+                if (PercentEncodingHelper.TryExtractBaggage([.. baggageCollection], out var baggage))
                 {
                     return new PropagationContext(context.ActivityContext, new Baggage(baggage!));
                 }
@@ -97,77 +90,12 @@ public override void Inject(PropagationContext context, T carrier, Action? baggage)
-    {
-        int baggageLength = -1;
-        bool done = false;
-        Dictionary? baggageDictionary = null;
-
-        foreach (var item in baggageCollection)
-        {
-            if (done)
-            {
-                break;
-            }
-
-            if (string.IsNullOrEmpty(item))
-            {
-                continue;
-            }
-
-            foreach (var pair in item.Split(CommaSignSeparator))
-            {
-                baggageLength += pair.Length + 1; // pair and comma
-
-                if (baggageLength >= MaxBaggageLength || baggageDictionary?.Count >= MaxBaggageItems)
-                {
-                    done = true;
-                    break;
-                }
-
-#if NET
-                if (pair.IndexOf('=', StringComparison.Ordinal) < 0)
-#else
-                if (pair.IndexOf('=') < 0)
-#endif
-                {
-                    continue;
-                }
-
-                var parts = pair.Split(EqualSignSeparator, 2);
-                if (parts.Length != 2)
-                {
-                    continue;
-                }
-
-                var key = WebUtility.UrlDecode(parts[0]);
-                var value = WebUtility.UrlDecode(parts[1]);
-
-                if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
-                {
-                    continue;
-                }
-
-                baggageDictionary ??= [];
-
-                baggageDictionary[key] = value;
-            }
-        }
-
-        baggage = baggageDictionary;
-        return baggageDictionary != null;
-    }
 }
diff --git a/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj b/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj
index a3a2895ee3e..ed92e7e7750 100644
--- a/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj
+++ b/src/OpenTelemetry.Api/OpenTelemetry.Api.csproj
@@ -15,6 +15,7 @@
     
     
     
+    
     
     
     
diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md
index 8ac2f28309d..9f2b37a7f34 100644
--- a/src/OpenTelemetry/CHANGELOG.md
+++ b/src/OpenTelemetry/CHANGELOG.md
@@ -18,6 +18,11 @@ Released 2025-Oct-01
   to a single `MeterProvider`, as required by the OpenTelemetry specification.
   ([#6458](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6458))
 
+* Added decoding of the `OTEL_RESOURCE_ATTRIBUTES` variable according to the specification,
+  adhering to the [W3C Baggage](https://github.com/w3c/baggage/blob/main/baggage/HTTP_HEADER_FORMAT.md)
+  format.
+  ([#6461](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6461))
+
 * Added `FormatMessage` configuration option to self-diagnostics feature. When
   set to `true` (default is false), log messages will be formatted by replacing
   placeholders with actual parameter values for improved readability.
diff --git a/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs b/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs
index 468b37ca1a9..72591889e75 100644
--- a/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs
+++ b/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs
@@ -2,14 +2,13 @@
 // SPDX-License-Identifier: Apache-2.0
 
 using Microsoft.Extensions.Configuration;
+using OpenTelemetry.Internal;
 
 namespace OpenTelemetry.Resources;
 
 internal sealed class OtelEnvResourceDetector : IResourceDetector
 {
     public const string EnvVarKey = "OTEL_RESOURCE_ATTRIBUTES";
-    private const char AttributeListSplitter = ',';
-    private const char AttributeKeyValueSplitter = '=';
 
     private readonly IConfiguration configuration;
 
@@ -35,16 +34,12 @@ private static List> ParseResourceAttributes(string
     {
         var attributes = new List>();
 
-        string[] rawAttributes = resourceAttributes.Split(AttributeListSplitter);
-        foreach (string rawKeyValuePair in rawAttributes)
+        if (PercentEncodingHelper.TryExtractBaggage([resourceAttributes], out var baggage) && baggage != null)
         {
-            string[] keyValuePair = rawKeyValuePair.Split(AttributeKeyValueSplitter);
-            if (keyValuePair.Length != 2)
+            foreach (var kvp in baggage)
             {
-                continue;
+                attributes.Add(new KeyValuePair(kvp.Key, kvp.Value));
             }
-
-            attributes.Add(new KeyValuePair(keyValuePair[0].Trim(), keyValuePair[1].Trim()));
         }
 
         return attributes;
diff --git a/src/Shared/PercentEncodingHelper.cs b/src/Shared/PercentEncodingHelper.cs
new file mode 100644
index 00000000000..9c94e560ef3
--- /dev/null
+++ b/src/Shared/PercentEncodingHelper.cs
@@ -0,0 +1,146 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#if NET
+using System.Diagnostics.CodeAnalysis;
+#endif
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace OpenTelemetry.Internal;
+
+/// 
+/// Helper methods for percent-encoding and decoding baggage values.
+/// See https://w3c.github.io/baggage/.
+/// 
+internal static partial class PercentEncodingHelper
+{
+    private const int MaxBaggageLength = 8192;
+    private const int MaxBaggageItems = 180;
+    private const char KeyValueSplitter = '=';
+    private const char ListSplitter = ',';
+
+    internal static bool TryExtractBaggage(
+        string[] baggageCollection,
+#if NET
+        [NotNullWhen(true)]
+#endif
+        out Dictionary? baggage)
+    {
+        Dictionary? baggageDictionary = null;
+        int baggageLength = -1; // Start with -1 to account for no leading comma on first item
+
+        foreach (var baggageList in baggageCollection.Where(h => !string.IsNullOrEmpty(h)))
+        {
+            foreach (string keyValuePair in baggageList.Split(ListSplitter))
+            {
+                baggageLength += keyValuePair.Length + 1; // pair length + comma
+                if (ExceedsMaxBaggageLimits(baggageLength, baggageDictionary?.Count))
+                {
+                    baggage = baggageDictionary;
+                    return baggageDictionary != null;
+                }
+#if NET
+                var indexOfFirstEquals = keyValuePair.IndexOf(KeyValueSplitter, StringComparison.Ordinal);
+#else
+                var indexOfFirstEquals = keyValuePair.IndexOf(KeyValueSplitter);
+#endif
+                if (indexOfFirstEquals < 0)
+                {
+                    continue;
+                }
+
+                var splitKeyValue = keyValuePair.Split([KeyValueSplitter], 2);
+                var key = splitKeyValue[0].Trim();
+                var value = splitKeyValue[1].Trim();
+
+                if (!IsValidKeyValuePair(key, value))
+                {
+                    continue;
+                }
+
+                var decodedValue = PercentDecodeBaggage(value);
+
+                baggageDictionary ??= [];
+                baggageDictionary[key] = decodedValue;
+            }
+        }
+
+        baggage = baggageDictionary;
+        return baggageDictionary != null;
+    }
+
+    /// 
+    /// As per the specification, only the value is percent-encoded.
+    /// "Uri.EscapeDataString" encodes code points which are not required to be percent-encoded.
+    /// 
+    ///  The baggage key. 
+    ///  The baggage value. 
+    ///  The percent-encoded baggage item. 
+    internal static string PercentEncodeBaggage(string key, string value) => $"{key.Trim()}={Uri.EscapeDataString(value.Trim())}";
+
+    private static string PercentDecodeBaggage(string baggageEncoded)
+    {
+        var bytes = new List();
+        for (int i = 0; i < baggageEncoded.Length; i++)
+        {
+            if (baggageEncoded[i] == '%' && i + 2 < baggageEncoded.Length && IsHex(baggageEncoded[i + 1]) && IsHex(baggageEncoded[i + 2]))
+            {
+                var hex = baggageEncoded.AsSpan(i + 1, 2);
+#if NET
+                bytes.Add(byte.Parse(hex, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture));
+#else
+                bytes.Add(Convert.ToByte(hex.ToString(), 16));
+#endif
+
+                i += 2;
+            }
+            else if (baggageEncoded[i] == '%')
+            {
+                return baggageEncoded; // Bad percent triplet -> return original value
+            }
+            else
+            {
+                if (!IsBaggageOctet(baggageEncoded[i]))
+                {
+                    return baggageEncoded; // non-encoded character not baggage octet encoded -> return original value
+                }
+
+                bytes.Add((byte)baggageEncoded[i]);
+            }
+        }
+
+        return new UTF8Encoding(false, false).GetString(bytes.ToArray());
+    }
+
+#if NET
+    [GeneratedRegex(@"^[!#$%&'*+\-\.^_`|~0-9A-Z]+$", RegexOptions.IgnoreCase)]
+    private static partial Regex TokenRegex();
+#else
+
+#pragma warning disable SA1201 // A field should not follow a method
+    private static readonly Regex TokenRegexField = new(
+        @"^[!#$%&'*+\-\.^_`|~0-9A-Z]+$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+#pragma warning restore SA1201 // A field should not follow a method
+
+    private static Regex TokenRegex() => TokenRegexField;
+#endif
+
+    private static bool ExceedsMaxBaggageLimits(int currentLength, int? currentItemCount) =>
+        currentLength >= MaxBaggageLength || currentItemCount >= MaxBaggageItems;
+
+    private static bool IsValidKeyValuePair(string key, string value) =>
+        !string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value) && TokenRegex().IsMatch(key);
+
+    private static bool IsHex(char c) =>
+        (c >= '0' && c <= '9') ||
+        (c >= 'a' && c <= 'f') ||
+        (c >= 'A' && c <= 'F');
+
+    private static bool IsBaggageOctet(char c) =>
+        c == 0x21 ||
+        (c >= 0x23 && c <= 0x2B) ||
+        (c >= 0x2D && c <= 0x3A) ||
+        (c >= 0x3C && c <= 0x5B) ||
+        (c >= 0x5D && c <= 0x7E);
+}
diff --git a/test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs b/test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs
index 13c21c28cec..e50758dea59 100644
--- a/test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs
+++ b/test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs
@@ -152,14 +152,14 @@ public void ValidateSpecialCharsBaggageExtraction()
 
         Assert.Equal(3, actualBaggage.Count);
 
-        Assert.True(actualBaggage.ContainsKey("key 1"));
-        Assert.Equal("value 1", actualBaggage["key 1"]);
+        Assert.True(actualBaggage.ContainsKey("key+1"));
+        Assert.Equal("value+1", actualBaggage["key+1"]);
 
         Assert.True(actualBaggage.ContainsKey("key2"));
         Assert.Equal("!x_x,x-x&x(x\");:", actualBaggage["key2"]);
 
-        Assert.True(actualBaggage.ContainsKey("key()3"));
-        Assert.Equal("value()!&;:", actualBaggage["key()3"]);
+        Assert.True(actualBaggage.ContainsKey("key%28%293"));
+        Assert.Equal("value()!&;:", actualBaggage["key%28%293"]);
     }
 
     [Fact]
@@ -204,6 +204,6 @@ public void ValidateSpecialCharsBaggageInjection()
         this.baggage.Inject(propagationContext, carrier, Setter);
 
         Assert.Single(carrier);
-        Assert.Equal("key+1=value+1,key2=!x_x%2Cx-x%26x(x%22)%3B%3A", carrier[BaggagePropagator.BaggageHeaderName]);
+        Assert.Equal("key 1=value%201,key2=%21x_x%2Cx-x%26x%28x%22%29%3B%3A", carrier[BaggagePropagator.BaggageHeaderName]);
     }
 }
diff --git a/test/OpenTelemetry.Api.Tests/PercentEncodingHelperTests.cs b/test/OpenTelemetry.Api.Tests/PercentEncodingHelperTests.cs
new file mode 100644
index 00000000000..129c1debeda
--- /dev/null
+++ b/test/OpenTelemetry.Api.Tests/PercentEncodingHelperTests.cs
@@ -0,0 +1,92 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using OpenTelemetry.Internal;
+using Xunit;
+
+namespace OpenTelemetry.Api.Tests;
+
+#pragma warning disable CA1062 // Validate arguments of public methods
+public class PercentEncodingHelperTests
+{
+    [Theory]
+    [InlineData(new string[] { "key1=val1,key2=val2", "key3=val3", "key4=val4" }, new string[] { "key1", "key2", "key3", "key4" }, new string[] { "val1", "val2", "val3", "val4" })] // Multiple headers
+    [InlineData(new string[] { "key1=val%201,key2=val2" }, new string[] { "key1", "key2" }, new string[] { "val 1", "val2" })]
+    [InlineData(new string[] { "key1,key2=val2" }, new string[] { "key2" }, new string[] { "val2" })]
+    [InlineData(new string[] { "key=Am%C3%A9lie" }, new string[] { "key" }, new string[] { "Am\u00E9lie" })] // Valid percent-encoded value
+    [InlineData(new string[] { "key1=val1,key2=val2==3" }, new string[] { "key1", "key2" }, new string[] { "val1", "val2==3" })] // Valid value with equal sign
+    [InlineData(new string[] { "key1=,key2=val2" }, new string[] { "key2" }, new string[] { "val2" })] // Empty value for key1
+    [InlineData(new string[] { "=val1,key2=val2" }, new string[] { "key2" }, new string[] { "val2" })] // Empty key for key1
+    [InlineData(new string[] { "Am\u00E9lie=val" }, new string[] { }, new string[] { }, false)] // Invalid key
+    [InlineData(new string[] { "key=invalid%encoding" }, new string[] { "key" }, new string[] { "invalid%encoding" })] // Invalid value
+    [InlineData(new string[] { "key=v1+v2" }, new string[] { "key" }, new string[] { "v1+v2" })]
+#if NET
+    [InlineData(new string[] { "key=a%E0%80Am%C3%A9lie" }, new string[] { "key" }, new string[] { "a\uFFFD\uFFFDAm\u00E9lie" })]
+#else
+    [InlineData(new string[] { "key=a%E0%80Am%C3%A9lie" }, new string[] { "key" }, new string[] { "a\uFFFDAm\u00E9lie" })]
+#endif
+    public void ValidateBaggageExtraction(string[] baggage, string[] expectedKey, string[] expectedValue, bool canExtractExpected = true)
+    {
+        var canExtract = PercentEncodingHelper.TryExtractBaggage(baggage, out var extractedBaggage);
+
+        Assert.Equal(canExtractExpected, canExtract);
+        if (!canExtractExpected)
+        {
+            Assert.Null(extractedBaggage);
+            return;
+        }
+
+        Assert.Equal(expectedKey.Length, extractedBaggage!.Count);
+        for (int i = 0; i < expectedKey.Length; i++)
+        {
+            Assert.True(extractedBaggage!.ContainsKey(expectedKey[i]));
+            Assert.Equal(expectedValue[i], extractedBaggage[expectedKey[i]]);
+        }
+    }
+
+    [Theory]
+    [InlineData("key1", "value 1", "key1=value%201")]
+    [InlineData("key2", "!x_x,x-x&x(x\");:", "key2=%21x_x%2Cx-x%26x%28x%22%29%3B%3A")]
+    [InlineData("key2", """!x_x,x-x&x(x\");:""", "key2=%21x_x%2Cx-x%26x%28x%5C%22%29%3B%3A")]
+    public void ValidateBaggageEncoding(string key, string value, string expectedEncoded)
+    {
+        var encodedValue = PercentEncodingHelper.PercentEncodeBaggage(key, value);
+        Assert.Equal(expectedEncoded, encodedValue);
+    }
+
+    [Fact]
+    public void ValidateBaggageExtraction_ExceedsItemLimit()
+    {
+        var baggageItems = new List();
+        for (int i = 0; i < 200; i++)
+        {
+            baggageItems.Add($"key{i}=value{i}");
+        }
+
+        var baggage = string.Join(",", baggageItems);
+        var canExtract = PercentEncodingHelper.TryExtractBaggage([baggage], out var extractedBaggage);
+
+        Assert.True(canExtract);
+        Assert.NotNull(extractedBaggage);
+        Assert.Equal(180, extractedBaggage!.Count); // Max 180 items
+        for (int i = 0; i < 180; i++)
+        {
+            Assert.True(extractedBaggage!.ContainsKey($"key{i}"));
+            Assert.Equal($"value{i}", extractedBaggage[$"key{i}"]);
+        }
+    }
+
+    [Fact]
+    public void ValidateBaggageExtraction_ExceedsLengthLimit()
+    {
+        var baggage = $"name={new string('x', 8186)},clientId=1234";
+        var canExtract = PercentEncodingHelper.TryExtractBaggage([baggage], out var extractedBaggage);
+
+        Assert.True(canExtract);
+        Assert.NotNull(extractedBaggage);
+
+        Assert.Single(extractedBaggage!); // Only one item should be extracted due to length limit
+        Assert.Equal("name", extractedBaggage!.Keys.First());
+        Assert.Equal(new string('x', 8186), extractedBaggage["name"]);
+    }
+}
diff --git a/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTests.cs b/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTests.cs
index 2f2c337d67e..de69070d7d7 100644
--- a/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTests.cs
+++ b/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTests.cs
@@ -6,6 +6,7 @@
 
 namespace OpenTelemetry.Resources.Tests;
 
+#pragma warning disable CA1062 // Validate arguments of public methods
 public sealed class OtelEnvResourceDetectorTests : IDisposable
 {
     public OtelEnvResourceDetectorTests()
@@ -37,26 +38,24 @@ public void OtelEnvResource_NullEnvVar()
         Assert.Equal(Resource.Empty, resource);
     }
 
-    [Fact]
-    public void OtelEnvResource_WithEnvVar_1()
-    {
-        // Arrange
-        var envVarValue = "Key1=Val1,Key2=Val2";
-        Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, envVarValue);
-        var resource = new OtelEnvResourceDetector(
-            new ConfigurationBuilder().AddEnvironmentVariables().Build())
-            .Detect();
-
-        // Assert
-        Assert.NotEqual(Resource.Empty, resource);
-        Assert.Contains(new KeyValuePair("Key1", "Val1"), resource.Attributes);
-    }
-
-    [Fact]
-    public void OtelEnvResource_WithEnvVar_2()
+    [Theory]
+    [InlineData("key1=val1,key2=val2", new string[] { "key1", "key2" }, new string[] { "val1", "val2" })]
+    [InlineData("key1,key2=val2", new string[] { "key2" }, new string[] { "val2" })]
+    [InlineData("key=Am%C3%A9lie", new string[] { "key" }, new string[] { "Am\u00E9lie" })] // Valid percent-encoded value
+    [InlineData("key1=val1,key2=val2==3", new string[] { "key1", "key2" }, new string[] { "val1", "val2==3" })] // Valid value with equal sign
+    [InlineData("key1=,key2=val2", new string[] { "key2" }, new string[] { "val2" })] // Empty value for key1
+    [InlineData("=val1,key2=val2", new string[] { "key2" }, new string[] { "val2" })] // Empty key for key1
+    [InlineData("Am\u00E9lie=val", new string[] { }, new string[] { })] // Invalid key
+    [InlineData("key=invalid%encoding", new string[] { "key" }, new string[] { "invalid%encoding" })] // Invalid value
+    [InlineData("key=v1+v2", new string[] { "key" }, new string[] { "v1+v2" })]
+#if NET
+    [InlineData("key=a%E0%80Am%C3%A9lie", new string[] { "key" }, new string[] { "a\uFFFD\uFFFDAm\u00E9lie" })]
+#else
+    [InlineData("key=a%E0%80Am%C3%A9lie", new string[] { "key" }, new string[] { "a\uFFFDAm\u00E9lie" })]
+#endif
+    public void OtelEnvResource_EnvVar_Validation(string envVarValue, string[] expectedKeys, string[] expectedValues)
     {
         // Arrange
-        var envVarValue = "Key1,Key2=Val2";
         Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, envVarValue);
         var resource = new OtelEnvResourceDetector(
             new ConfigurationBuilder().AddEnvironmentVariables().Build())
@@ -64,8 +63,12 @@ public void OtelEnvResource_WithEnvVar_2()
 
         // Assert
         Assert.NotEqual(Resource.Empty, resource);
-        Assert.Single(resource.Attributes);
-        Assert.Contains(new KeyValuePair("Key2", "Val2"), resource.Attributes);
+        Assert.Equal(expectedKeys.Length, expectedValues.Length);
+        Assert.Equal(expectedKeys.Length, resource.Attributes.Count());
+        for (int i = 0; i < expectedKeys.Length; i++)
+        {
+            Assert.Equal(expectedKeys.Zip(expectedValues, (k, v) => new KeyValuePair(k, v)), resource.Attributes);
+        }
     }
 
     [Fact]