---
title: "Presigned URLs"
description: "Create time-limited presigned URLs for Edge Network storage so users can upload or download objects directly, using AWS S3 SDKs or the Edge API endpoint."
url: "https://edge.network/docs/storage/presigned-urls"
section: "Storage"
---

# Presigned URLs

Generate time-limited signed URLs that allow anyone to upload or download objects directly, without exposing your credentials. Ideal for browser uploads, mobile apps and sharing private files.

## How presigned URLs work

A presigned URL is a time-limited, self-authenticating URL that grants temporary access to a specific object. The URL embeds the access credentials as query parameters using AWS Signature V4, so the recipient doesn't need API keys.

- **Presigned uploads:** let users upload files directly to storage from their browser or app. No server-side proxy is needed; the client PUTs directly to the signed URL.
- **Presigned downloads:** share private files with time-limited links. Give a user a URL to download their invoice, report or asset. The link expires after the set duration.

## Two ways to generate presigned URLs

- **Using S3 SDKs (recommended):** generate presigned URLs client-side or server-side using any AWS S3 SDK. This is the standard approach and works identically to AWS S3. Requires your access key and secret key.
- **Using the Edge API:** call `POST /api/storage/buckets/:name/presign` with your Edge API token. The server generates the presigned URL for you, so no S3 credentials are needed on the client.

## SDK examples

For JavaScript and Node.js, install the S3 client and presigner:

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

### Generate a presigned upload URL

```js tab="JavaScript"
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({
  endpoint: 'https://storage.edge.network',
  region: 'us-east-1',
  forcePathStyle: true,
  credentials: {
    accessKeyId: process.env.EDGE_ACCESS_KEY,
    secretAccessKey: process.env.EDGE_SECRET_KEY
  }
})

// Generate a presigned upload URL (valid for 1 hour)
const url = await getSignedUrl(client, new PutObjectCommand({
  Bucket: 'my-bucket',
  Key: 'uploads/photo.jpg',
  ContentType: 'image/jpeg',
}), { expiresIn: 3600 })
```

```python tab="Python"
import os
import boto3

client = boto3.client(
    's3',
    endpoint_url='https://storage.edge.network',
    aws_access_key_id=os.environ['EDGE_ACCESS_KEY'],
    aws_secret_access_key=os.environ['EDGE_SECRET_KEY'],
    region_name='us-east-1'
)

# Generate a presigned upload URL (valid for 1 hour)
url = client.generate_presigned_url(
    'put_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'uploads/photo.jpg',
        'ContentType': 'image/jpeg',
    },
    ExpiresIn=3600
)

print(url)
```

### Generate a presigned download URL

Both examples reuse the `client` created above.

```js tab="JavaScript"
import { GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

// Generate a presigned download URL (valid for 15 minutes)
const url = await getSignedUrl(client, new GetObjectCommand({
  Bucket: 'my-bucket',
  Key: 'invoices/invoice-001.pdf',
}), { expiresIn: 900 })
```

```python tab="Python"
# Generate a presigned download URL (valid for 15 minutes)
url = client.generate_presigned_url(
    'get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'invoices/invoice-001.pdf',
    },
    ExpiresIn=900
)

print(url)
```

### Upload from a browser

Upload a file from the browser using the presigned URL:

```js
// Browser: upload a file using the presigned URL
const fileInput = document.querySelector('input[type="file"]')
const file = fileInput.files[0]

const response = await fetch(presignedUrl, {
  method: 'PUT',
  body: file,
  headers: {
    'Content-Type': file.type,
  },
})

if (response.ok) {
  console.log('Upload complete')
}
```

### Use a presigned URL with cURL

Upload with a presigned URL:

```bash
# Upload using the presigned URL
curl -X PUT "$PRESIGNED_URL" \
  -H "Content-Type: image/jpeg" \
  --data-binary @photo.jpg
```

Download with a presigned URL:

```bash
# Download using the presigned URL
curl -o invoice.pdf "$PRESIGNED_URL"
```

## Edge API

If you don't want to manage S3 credentials on the client, you can generate presigned URLs server-side via the Edge API using your account's Bearer token.

### `POST /api/storage/buckets/:name/presign`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `key` | string | Yes | Object key (path) within the bucket |
| `action` | string | No | `download` (default) or `upload` |
| `expires` | string | No | Duration: `15m`, `1h` (default), `7d` (max) |

Generate an upload URL:

```bash
# Generate a presigned upload URL via the Edge API
curl -X POST https://api.edge.network/api/storage/buckets/my-bucket/presign \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "uploads/photo.jpg",
    "action": "upload",
    "expires": "1h"
  }'
```

Response:

```json
{
  "url": "https://storage.edge.network/my-bucket/uploads/photo.jpg?X-Amz-Algorithm=...",
  "method": "PUT",
  "expires": "2026-03-12T01:00:00Z"
}
```

Generate a download URL:

```bash
# Generate a presigned download URL via the Edge API
curl -X POST https://api.edge.network/api/storage/buckets/my-bucket/presign \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "invoices/invoice-001.pdf",
    "expires": "15m"
  }'
```

## Common use cases

- **User avatar uploads:** generate a presigned PUT URL server-side, return it to the browser, and let the user upload their photo directly to storage.
- **Private file sharing:** generate a presigned GET URL for an invoice or report and send the link to the user. It expires after the set duration.
- **Mobile app uploads:** your backend generates a presigned URL, sends it to the mobile app, and the app uploads directly. No proxy, no extra bandwidth on your server.
- **Third-party integrations:** give a partner or webhook a presigned URL to upload data into your bucket without sharing credentials.

## Security considerations

> [!WARNING]
> - **URLs are bearer tokens.** Anyone with the URL can use it until it expires. Share them over secure channels (HTTPS, encrypted messages).
> - **Keep expiry times short.** Use the minimum duration needed: 15 minutes for downloads, 1 hour for uploads. Maximum is 7 days.
> - **URLs are scoped to a single object.** A presigned URL for `photos/image.jpg` cannot be used to access any other object.
> - **Revoking access.** Delete or rotate the access key used to generate the URL. All presigned URLs created with that key become invalid immediately.

## Next steps

- [SDK examples](/docs/storage/sdk-examples) — Connect AWS S3 SDKs to Edge Storage
- [Multipart uploads](/docs/storage/multipart-uploads) — Upload large files reliably
