Skip to content

Commit 4aa433f

Browse files
harshil1712Oxyjun
authored andcommitted
[R2] Update Presigned URL docs (#21975)
* update Presigned URL vs Binding * Apply suggestions from code review * update limitations * Apply suggestions from code review * Cleaning up structure of the doc with tabs. * Small fix * Further language clarification --------- Co-authored-by: Jun Lee <[email protected]>
1 parent 68106cc commit 4aa433f

File tree

1 file changed

+74
-40
lines changed

1 file changed

+74
-40
lines changed

src/content/docs/r2/api/s3/presigned-urls.mdx

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: Presigned URLs
33
pcx_content_type: concept
44
---
55

6+
import {Tabs, TabItem } from "~/components";
7+
68
Presigned URLs are an [S3 concept](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html) for sharing direct access to your bucket without revealing your token secret. A presigned URL authorizes anyone with the URL to perform an action to the S3 compatibility endpoint for an R2 bucket. By default, the S3 endpoint requires an `AUTHORIZATION` header signed by your token. Every presigned URL has S3 parameters and search parameters containing the signature information that would be present in an `AUTHORIZATION` header. The performable action is restricted to a specific resource, an [operation](/r2/api/s3/api/), and has an associated timeout.
79

810
There are three kinds of resources in R2:
@@ -50,16 +52,6 @@ R2 currently supports the following methods when generating a presigned URL:
5052

5153
`POST`, which performs uploads via native HTML forms, is not currently supported.
5254

53-
## Generate presigned URLs
54-
55-
Generate a presigned URL by referring to the following examples:
56-
57-
- [AWS SDK for Go](/r2/examples/aws/aws-sdk-go/#generate-presigned-urls)
58-
- [AWS SDK for JS v3](/r2/examples/aws/aws-sdk-js-v3/#generate-presigned-urls)
59-
- [AWS SDK for JS](/r2/examples/aws/aws-sdk-js/#generate-presigned-urls)
60-
- [AWS SDK for PHP](/r2/examples/aws/aws-sdk-php/#generate-presigned-urls)
61-
- [AWS CLI](/r2/examples/aws/aws-cli/#generate-presigned-urls)
62-
6355
## Presigned URL alternative with Workers
6456

6557
A valid alternative design to presigned URLs is to use a Worker with a [binding](/workers/runtime-apis/bindings/) that implements your security policy.
@@ -72,14 +64,33 @@ A binding is defined in the Wrangler file of your Worker project's directory.
7264

7365
:::
7466

75-
A possible use case may be restricting an application to only be able to upload to a specific URL. With presigned URLs, your central signing application might look like the following JavaScript code running on Cloudflare Workers, workerd, or another platform.
67+
Refer to [Use R2 from Workers](/r2/api/workers/workers-api-usage/) to learn how to bind a bucket to a Worker and use the binding to interact with your bucket.
7668

77-
If the Worker received a request for `https://example.com/uploads/dog.png`, it would respond with a presigned URL allowing a user to upload to your R2 bucket at the `/uploads/dog.png` path.
69+
## Generate presigned URLs
70+
71+
Generate a presigned URL by referring to the following examples:
72+
73+
- [AWS SDK for Go](/r2/examples/aws/aws-sdk-go/#generate-presigned-urls)
74+
- [AWS SDK for JS v3](/r2/examples/aws/aws-sdk-js-v3/#generate-presigned-urls)
75+
- [AWS SDK for JS](/r2/examples/aws/aws-sdk-js/#generate-presigned-urls)
76+
- [AWS SDK for PHP](/r2/examples/aws/aws-sdk-php/#generate-presigned-urls)
77+
- [AWS CLI](/r2/examples/aws/aws-cli/#generate-presigned-urls)
78+
79+
### Example of generating presigned URLs
80+
81+
A possible use case may be restricting an application to only be able to upload to a specific URL. With presigned URLs, your central signing application might look like the following JavaScript code running on Cloudflare Workers, `workerd`, or another platform (you might have to update the code based on the platform you are using).
82+
83+
If the application received a request for `https://example.com/uploads/dog.png`, it would respond with a presigned URL allowing a user to upload to your R2 bucket at the `/uploads/dog.png` path.
84+
85+
To create a presigned URL, you will need to either use a package that implements the signing algorithm, or implement the signing algorithm yourself. In this example, the `aws4fetch` package is used. You also need to have an access key ID and a secret access key. Refer to [R2 API tokens](/r2/api/tokens/) for more information.
7886

7987
```ts
8088
import { AwsClient } from "aws4fetch";
8189

82-
const r2 = new AwsClient({
90+
// Create a new client
91+
// Replace with your own access key ID and secret access key
92+
// Make sure to store these securely and not expose them
93+
const client = new AwsClient({
8394
accessKeyId: "",
8495
secretAccessKey: "",
8596
});
@@ -98,6 +109,7 @@ export default {
98109
return new Response("Missing a filepath", { status: 400 });
99110
}
100111

112+
// Replace with your bucket name and account ID
101113
const bucketName = "";
102114
const accountId = "";
103115

@@ -111,7 +123,7 @@ export default {
111123
// Specify a custom expiry for the presigned URL, in seconds
112124
url.searchParams.set("X-Amz-Expires", "3600");
113125

114-
const signed = await r2.sign(
126+
const signed = await client.sign(
115127
new Request(url, {
116128
method: "PUT",
117129
}),
@@ -128,12 +140,47 @@ export default {
128140
} satisfies ExportedHandler;
129141
```
130142

131-
Notice the total absence of any configuration or token secrets present in the Worker code. Instead, in your [Wrangler configuration file](/workers/wrangler/configuration/), you would create a [binding](/r2/api/workers/workers-api-usage/#3-bind-your-bucket-to-a-worker) to whatever bucket represents the bucket you will upload to. Additionally, authorization is handled in-line with the upload which can reduce latency.
143+
## Differences between presigned URLs and R2 binding
144+
145+
- When using an R2 binding, you will not need any token secrets in your Worker code. Instead, in your [Wrangler configuration file](/workers/wrangler/configuration/), you will create a [binding](/r2/api/workers/workers-api-usage/#3-bind-your-bucket-to-a-worker) to your R2 bucket. Additionally, authorization is handled in-line, which can reduce latency.
146+
- When using presigned URLs, you will need to create and use the token secrets in your Worker code.
147+
148+
In some cases, R2 bindings let you implement certain functionality more easily. For example, if you wanted to offer a write-once guarantee so that users can only upload to a path once:
149+
150+
- With R2 binding: You only need to pass the header once.
151+
- With presigned URLs: You need to first sign specific headers, then request the user to send the same headers.
132152

133-
In some cases, Workers lets you implement certain functionality more easily. For example, if you wanted to offer a write-once guarantee so that users can only upload to a path once, with pre-signed URLs, you would need to sign specific headers and require the sender to send them. You can modify the previous Worker to sign additional headers:
153+
<Tabs>
154+
<TabItem label="R2 binding example">
155+
156+
If you are using R2 bindings, you would change your upload to:
157+
158+
```ts
159+
const existingObject = await env.R2_BUCKET.put(key, request.body, {
160+
onlyIf: {
161+
// No objects will have been uploaded before September 28th, 2021 which
162+
// is the initial R2 announcement.
163+
uploadedBefore: new Date(1632844800000),
164+
},
165+
});
166+
if (existingObject?.etag !== request.headers.get("etag")) {
167+
return new Response("attempt to overwrite object", { status: 400 });
168+
}
169+
```
170+
171+
When using R2 bindings, you may need to consider the following limitations:
172+
173+
- You cannot upload more than 100 MiB (200 MiB for Business customers) when using R2 bindings.
174+
- Enterprise customers can upload 500 MiB by default and can ask their account team to raise this limit.
175+
- Detecting [precondition failures](/r2/api/s3/extensions/#conditional-operations-in-putobject) is currently easier with presigned URLs as compared with R2 bindings.
176+
177+
Note that these limitations depend on R2's extension for conditional uploads. Amazon's S3 service does not offer such functionality at this time.
178+
</TabItem>
179+
<TabItem label="Presigned URL example">
180+
You can modify the previous example to sign additional headers:
134181

135182
```ts
136-
const signed = await r2.sign(
183+
const signed = await client.sign(
137184
new Request(url, {
138185
method: "PUT",
139186
}),
@@ -146,34 +193,21 @@ const signed = await r2.sign(
146193
);
147194
```
148195

149-
Note that the caller has to add the same `If-Unmodified-Since` header to use the URL. The caller cannot omit the header or use a different header. If the caller uses a different header, the presigned URL signature would not match, and they would receive a `403/SignatureDoesNotMatch`.
150-
151-
In a Worker, you would change your upload to:
152-
153196
```ts
154-
const existingObject = await env.DROP_BOX_BUCKET.put(
155-
url.toString().substring(1),
156-
request.body,
157-
{
158-
onlyIf: {
159-
// No objects will have been uploaded before September 28th, 2021 which
160-
// is the initial R2 announcement.
161-
uploadedBefore: new Date(1632844800000),
162-
},
197+
// Use the presigned URL to upload the file
198+
const response = await fetch(signed.url, {
199+
method: "PUT",
200+
body: file,
201+
headers: {
202+
"If-Unmodified-Since": "Tue, 28 Sep 2021 16:00:00 GMT",
163203
},
164-
);
165-
if (existingObject?.etag !== request.headers.get("etag")) {
166-
return new Response("attempt to overwrite object", { status: 400 });
167-
}
204+
});
168205
```
169206

170-
Cloudflare Workers currently have some limitations that you may need to consider:
171-
172-
- You cannot upload more than 100 MiB (200 MiB for Business customers) to a Worker.
173-
- Enterprise customers can upload 500 MiB by default and can ask their account team to raise this limit.
174-
- Detecting [precondition failures](/r2/api/s3/extensions/#conditional-operations-in-putobject) is currently easier with presigned URLs as compared with R2 bindings.
207+
Note that the caller has to add the same `If-Unmodified-Since` header to use the URL. The caller cannot omit the header or use a different header, since the signature covers the headers. If the caller uses a different header, the presigned URL signature would not match, and they would receive a `403/SignatureDoesNotMatch`.
175208

176-
Note that these limitations depends on R2's extension for conditional uploads. Amazon's S3 service does not offer such functionality at this time.
209+
</TabItem>
210+
</Tabs>
177211

178212
## Differences between presigned URLs and public buckets
179213

0 commit comments

Comments
 (0)