Skip to content

fix: encode + as %2B (not %20) in S3 canonical path#114

Open
Ortes wants to merge 1 commit into
xtyxtyx:masterfrom
Ortes:fix/plus-sign-path-encoding
Open

fix: encode + as %2B (not %20) in S3 canonical path#114
Ortes wants to merge 1 commit into
xtyxtyx:masterfrom
Ortes:fix/plus-sign-path-encoding

Conversation

@Ortes

@Ortes Ortes commented May 31, 2026

Copy link
Copy Markdown

Problem

encodePath treats + the same as space, writing %20 for both:

// Special handling for space and plus sign
if (char == ' '.codeUnitAt(0) || char == '+'.codeUnitAt(0)) {
  result.write('%20');
  continue;
}

AWS SigV4 requires + in a URI path to be percent-encoded as %2B; %20 is correct only for space. This mismatch causes a signature verification failure (HTTP 403) for any S3 object whose key contains a literal + — for example Movie Title HDR10+ BluRay.mkv. The canonical request path used for signing diverges from what S3 computes on its side, so the signatures never match.

Both statObject (existence checks) and presigned GET URLs are affected because both go through getCanonicalRequestencodePath.

Fix

Remove + from the space special-case so it falls through to the generic percent-encoding block, which correctly emits %2B:

if (char == ' '.codeUnitAt(0)) {
  result.write('%20');
  continue;
}

References

  • AWS SigV4 canonical URI spec: "URI encode every byte … The space character is a reserved character and must be encoded as %20 (and not as +)"
  • RFC 3986 §2.2/§2.3: + is a sub-delimiter and must be percent-encoded when it appears literally in a path component.

encodePath treated '+' the same as space, writing '%20' for both.
AWS SigV4 requires '+' in a URI path to be percent-encoded as '%2B';
'%20' is correct only for space. This mismatch caused signature
verification failures (403) for any S3 object key containing a literal
'+' (e.g. 'HDR10+ BluRay'), since the canonical request path used for
signing diverged from what S3 computed on its side.

Fix: remove '+' from the space special-case so it falls through to the
generic percent-encoding block, which correctly emits '%2B'.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant