---
# System prepended metadata

title: crates.io – Issue 4891

---

# crates.io – Issue 4891

<https://github.com/rust-lang/crates.io/issues/4891>

## Issue

> Some clients, proxies, and/or repository managers may encode URLs to the upstream (for instance JFrog Artifactory). Due to blocks on crates.io some of these requests fail if there is a `+` sign in the URL
>
> e.g. If we go to this URL it works fine:
>
> <https://static.crates.io/crates/libgit2-sys/libgit2-sys-0.12.25+1.3.0.crate>
>
> But if we go to the encoded URL it gives us a 403 Access Denied
> <https://static.crates.io/crates/libgit2-sys/libgit2-sys-0.12.25%2B1.3.0.crate>
>
> This has also exposed some more strange behavior.
If we encode the + sign as a space (%20 instead of %2B), it works.
> <https://static.crates.io/crates/libgit2-sys/libgit2-sys-0.12.25%201.3.0.crate>
>
> Or even if I put a space instead in the URL (though that may be my browser encoding for me)
> "<https://static.crates.io/crates/libgit2-sys/libgit2-sys-0.12.25> 1.3.0.crate"
>
> Since URL encoding is fairly standard and other encoding works, I believe this is a bug

## Investigation

### Current State

The following tests were performed using `curl -I <url>` against the staging
environment, whose configuration is identical to the production environment.

| Target     | Encoding | Result     |
|------------|----------|------------|
| CloudFront | `+`      | ✅ HTTP 200 |
| CloudFront | `%2B`    | ❌ HTTP 403 |
| CloudFront | `%20`    | ✅ HTTP 200 |
| Fastly     | `+`      | ✅ HTTP 200 |
| Fastly     | `%2B`    | ❌ HTTP 403 |
| Fastly     | `%20`    | ✅ HTTP 200 |
| S3         | `+`      | ✅ HTTP 200 |
| S3         | `%B`     | ❌ HTTP 403 |
| S3         | `%20`    | ✅ HTTP 200 |

### After Renaming the File

The filename was manually changed to `rust-cratesio-4891-0.1.0+1.crate`, and the
same tests as above were performed.

| Target     | Encoding | Result     |
|------------|----------|------------|
| CloudFront | `+`      | ❌ HTTP 403 |
| CloudFront | `%2B`    | ✅ HTTP 200 |
| CloudFront | `%20`    | ❌ HTTP 403 |
| Fastly     | `+`      | ❌ HTTP 403 |
| Fastly     | `%2B`    | ✅ HTTP 200 |
| Fastly     | `%20`    | ❌ HTTP 403 |
| S3         | `+`      | ❌ HTTP 403 |
| S3         | `%2B`    | ✅ HTTP 200 |
| S3         | `%20`    | ❌ HTTP 403 |

## Cause

The issue seems to be that the `+` is a special character on S3. It is converted to a space when files are uploaded to S3, e.g. `libgit2-sys-0.12.25+1.3.0.crate` is saved on S3 as `libgit2-sys-0.12.25 1.3.0.crate`.

When accessing a file in S3, a `+` in the URL is encoded to a space in the file name. When encoding the `+` as `%2B` before sending the request, S3 decodes it to the character `+` and looks for the file `libgit2-sys-0.12.25+1.3.0.crate`. This file does not exist, though, since the `+` in the original file name got decoded to a space.

The issue arises when third-party clients try to download a crate and `urlencode()` the name before sending the request.

## Solution

Since filenames with spaces in them can cause all sorts of issues, we want to store the crates with a name like `libgit2-sys-0.12.25+1.3.0.crate` in S3. This requires that names are URL-encoded prior to uploading crates.
