Skip to content

Commit a6079a4

Browse files
committed
update Presigned URL vs Binding
1 parent b81ee16 commit a6079a4

File tree

1 file changed

+52
-31
lines changed

1 file changed

+52
-31
lines changed

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

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,6 @@ R2 currently supports the following methods when generating a presigned URL:
5050

5151
`POST`, which performs uploads via native HTML forms, is not currently supported.
5252

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-
6353
## Presigned URL alternative with Workers
6454

6555
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 +62,33 @@ A binding is defined in the Wrangler file of your Worker project's directory.
7262

7363
:::
7464

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.
65+
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.
66+
67+
## Generate presigned URLs
68+
69+
Generate a presigned URL by referring to the following examples:
70+
71+
- [AWS SDK for Go](/r2/examples/aws/aws-sdk-go/#generate-presigned-urls)
72+
- [AWS SDK for JS v3](/r2/examples/aws/aws-sdk-js-v3/#generate-presigned-urls)
73+
- [AWS SDK for JS](/r2/examples/aws/aws-sdk-js/#generate-presigned-urls)
74+
- [AWS SDK for PHP](/r2/examples/aws/aws-sdk-php/#generate-presigned-urls)
75+
- [AWS CLI](/r2/examples/aws/aws-cli/#generate-presigned-urls)
76+
77+
### Example of generating presigned URLs
78+
79+
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).
7680

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.
81+
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.
82+
83+
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 secret access key. Refer to [R2 API tokens](/r2/api/tokens/) for more information.
7884

7985
```ts
8086
import { AwsClient } from "aws4fetch";
8187

82-
const r2 = new AwsClient({
88+
// Create a new client
89+
// Replace with your own access key ID and secret access key
90+
// Make sure to store these securely and not expose them
91+
const client = new AwsClient({
8392
accessKeyId: "",
8493
secretAccessKey: "",
8594
});
@@ -98,6 +107,7 @@ export default {
98107
return new Response("Missing a filepath", { status: 400 });
99108
}
100109

110+
// Replace with your bucket name and account ID
101111
const bucketName = "";
102112
const accountId = "";
103113

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

114-
const signed = await r2.sign(
124+
const signed = await client.sign(
115125
new Request(url, {
116126
method: "PUT",
117127
}),
@@ -128,12 +138,16 @@ export default {
128138
} satisfies ExportedHandler;
129139
```
130140

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.
141+
## Differences between presigned URLs and R2 binding
142+
143+
When using the 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.
132144

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:
145+
However, when using presigned URLs, you will need to create and use the token secrets in your Worker code.
146+
147+
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 the same headers. You can modify the previous example to sign additional headers:
134148

135149
```ts
136-
const signed = await r2.sign(
150+
const signed = await client.sign(
137151
new Request(url, {
138152
method: "PUT",
139153
}),
@@ -146,34 +160,41 @@ const signed = await r2.sign(
146160
);
147161
```
148162

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`.
163+
```ts
164+
// Use the presigned URL to upload the file
165+
const response = await fetch(signed.url, {
166+
method: "PUT",
167+
body: file,
168+
headers: {
169+
"If-Unmodified-Since": "Tue, 28 Sep 2021 16:00:00 GMT",
170+
},
171+
});
172+
```
173+
174+
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`.
150175

151-
In a Worker, you would change your upload to:
176+
If you are using R2 bindings, you would change your upload to:
152177

153178
```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-
},
179+
const existingObject = await env.R2_BUCKET.put(key, request.body, {
180+
onlyIf: {
181+
// No objects will have been uploaded before September 28th, 2021 which
182+
// is the initial R2 announcement.
183+
uploadedBefore: new Date(1632844800000),
163184
},
164-
);
185+
});
165186
if (existingObject?.etag !== request.headers.get("etag")) {
166187
return new Response("attempt to overwrite object", { status: 400 });
167188
}
168189
```
169190

170191
Cloudflare Workers currently have some limitations that you may need to consider:
171192

172-
- You cannot upload more than 100 MiB (200 MiB for Business customers) to a Worker.
193+
- You cannot upload more than 100 MiB (200 MiB for Business customers) with a Worker.
173194
- Enterprise customers can upload 500 MiB by default and can ask their account team to raise this limit.
174195
- Detecting [precondition failures](/r2/api/s3/extensions/#conditional-operations-in-putobject) is currently easier with presigned URLs as compared with R2 bindings.
175196

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.
197+
Note that these limitations depend on R2's extension for conditional uploads. Amazon's S3 service does not offer such functionality at this time.
177198

178199
## Differences between presigned URLs and public buckets
179200

0 commit comments

Comments
 (0)