diff --git a/multiaddr_test.go b/multiaddr_test.go
index b33e94d..8686cd9 100644
--- a/multiaddr_test.go
+++ b/multiaddr_test.go
@@ -627,6 +627,7 @@ func TestRoundTrip(t *testing.T) {
 		"/ip4/127.0.0.1/udp/1234/quic-v1/webtransport/certhash/uEiDDq4_xNyDorZBH3TlGazyJdOWSwvo4PUo5YHFMrvDE8g",
 		"/p2p/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP",
 		"/p2p/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP/unix/a/b/c",
+		"/http-path/tmp%2Fbar",
 	} {
 		ma, err := NewMultiaddr(s)
 		if err != nil {
@@ -923,3 +924,36 @@ func TestDNS(t *testing.T) {
 		t.Fatal("expected equality")
 	}
 }
+
+func TestHTTPPath(t *testing.T) {
+	t.Run("bad addr", func(t *testing.T) {
+		badAddr := "/http-path/thisIsMissingAfullBytes%f"
+		_, err := NewMultiaddr(badAddr)
+		require.Error(t, err)
+	})
+
+	t.Run("round trip", func(t *testing.T) {
+		cases := []string{
+			"/http-path/tmp%2Fbar",
+			"/http-path/tmp%2Fbar%2Fbaz",
+			"/http-path/foo",
+		}
+		for _, c := range cases {
+			m, err := NewMultiaddr(c)
+			require.NoError(t, err)
+			require.Equal(t, c, m.String())
+		}
+	})
+
+	t.Run("value for protocol", func(t *testing.T) {
+		m := StringCast("/http-path/tmp%2Fbar")
+		v, err := m.ValueForProtocol(P_HTTP_PATH)
+		require.NoError(t, err)
+		// This gives us the url escaped version
+		require.Equal(t, "tmp%2Fbar", v)
+
+		// If we want the raw unescaped version, we can use the component and read it
+		_, component := SplitLast(m)
+		require.Equal(t, "tmp/bar", string(component.RawValue()))
+	})
+}
diff --git a/protocols.go b/protocols.go
index b01e6cb..d3117b2 100644
--- a/protocols.go
+++ b/protocols.go
@@ -26,6 +26,7 @@ const (
 	P_P2P               = 421
 	P_IPFS              = P_P2P // alias for backwards compatibility
 	P_HTTP              = 480
+	P_HTTP_PATH         = 481
 	P_HTTPS             = 443 // deprecated alias for /tls/http
 	P_ONION             = 444 // also for backwards compatibility
 	P_ONION3            = 445
@@ -206,6 +207,13 @@ var (
 		Code:  P_HTTP,
 		VCode: CodeToVarint(P_HTTP),
 	}
+	protoHTTPPath = Protocol{
+		Name:       "http-path",
+		Code:       P_HTTP_PATH,
+		VCode:      CodeToVarint(P_HTTP_PATH),
+		Size:       LengthPrefixedVarSize,
+		Transcoder: TranscoderHTTPPath,
+	}
 	protoHTTPS = Protocol{
 		Name:  "https",
 		Code:  P_HTTPS,
@@ -301,6 +309,7 @@ func init() {
 		protoWEBTRANSPORT,
 		protoCERTHASH,
 		protoHTTP,
+		protoHTTPPath,
 		protoHTTPS,
 		protoP2P,
 		protoUNIX,
diff --git a/transcoders.go b/transcoders.go
index 5387740..18d7a5a 100644
--- a/transcoders.go
+++ b/transcoders.go
@@ -8,6 +8,7 @@ import (
 	"errors"
 	"fmt"
 	"net"
+	"net/url"
 	"strconv"
 	"strings"
 
@@ -454,3 +455,18 @@ func validateCertHash(b []byte) error {
 	_, err := mh.Decode(b)
 	return err
 }
+
+var TranscoderHTTPPath = NewTranscoderFromFunctions(httpPathStB, httpPathBtS, validateHTTPPath)
+
+func httpPathStB(s string) ([]byte, error) {
+	unescaped, err := url.QueryUnescape(s)
+	return []byte(unescaped), err
+}
+
+func httpPathBtS(b []byte) (string, error) {
+	return url.QueryEscape(string(b)), nil
+}
+
+func validateHTTPPath(b []byte) error {
+	return nil // We can represent any byte slice when we escape it.
+}