---
title: "Cómo generar URL pre-firmadas para Amazon S3 con Astro en Cloudflare Workers"
description: "¿Cómo generar URLs pre-firmadas para recuperar y subir a Amazon S3 con Astro en Cloudflare Workers?"
source: "https://www.launchfa.st/es/blog/aws-s3-storage-cloudflare-workers"
author: "Rishi Raj Jain"
created_at: 2025-04-23T00:00:00.000Z
---

> **Site index for agents**
> Fetch https://www.launchfa.st/llms.txt to discover every page on this site.
> Every page is also available as Markdown by appending `.md` to its URL.

<img width="1600" height="900" alt="Cómo Generar URLs Pre-firmadas para Amazon S3 con Cloudflare Workers" decoding="async" loading="eager" class="mt-4 border rounded-sm bg-cover bg-center bg-no-repeat transform will-change-auto" src="https://ik.imagekit.io/vjeqenuhn/launchfast-website/aws-s3-storage-cloudflare-workers.png" />

En esta guía, aprenderás cómo generar URLs pre-firmadas para Amazon S3 con Astro en Cloudflare Workers. Pasarás por el proceso de configurar un nuevo proyecto Astro, habilitar la renderización del lado del servidor usando el adaptador de Cloudflare, obtener credenciales de AWS y luego crear funciones para generar URLs pre-firmadas para la recuperación y carga desde Amazon S3.

## Prerrequisitos

Para seguir, necesitarás:

- [Node.js 20](https://nodejs.org/en) o posterior
- Una cuenta de [AWS](https://aws.amazon.com/free)

## Tabla de Contenidos

- [Crear una nueva aplicación Astro](#crear-una-nueva-aplicación-astro)
- [Integrar el adaptador de Cloudflare en tu proyecto Astro](#integrar-el-adaptador-de-cloudflare-en-tu-proyecto-astro)
- [Crear claves de acceso para usuarios IAM](#crear-claves-de-acceso-para-usuarios-iam)
- [Configurar la Política del Bucket de Amazon S3](#configurar-la-política-del-bucket-de-amazon-s3)
- [Configurar CORS de Amazon S3](#configurar-cors-de-amazon-s3)
- [Generar las URLs pre-firmadas](#generar-las-urls-pre-firmadas)
  - [1. Acceder a las Variables de Entorno](#1-acceder-a-las-variables-de-entorno)
  - [2. Definir el Cliente AWS](#2-definir-el-cliente-aws)
  - [3. Determinar URLs de S3](#3-determinar-urls-de-s3)
  - [4. URL pre-firmada para GET un Objeto S3 (recuperar)](#4-url-pre-firmada-para-get-un-objeto-s3-recuperar)
  - [5. URL pre-firmada para PUT un Objeto S3 (cargar)](#5-url-pre-firmada-para-put-un-objeto-s3-cargar)
  - [6. Crear un Endpoint del Servidor (una Ruta API) en Astro](#6-crear-un-endpoint-del-servidor-una-ruta-api-en-astro)
- [Desplegar en Cloudflare Workers](#desplegar-en-cloudflare-workers)

## Crear una nueva aplicación Astro

Comencemos creando un nuevo proyecto Astro. Abre tu terminal y ejecuta el siguiente comando:

```bash
npm create astro@latest my-app
```

`npm create astro` es la forma recomendada de crear rápidamente un proyecto Astro.

Cuando se te pida, elige:

- `Usar plantilla mínima (vacía)` cuando se te pregunte cómo iniciar el nuevo proyecto.
- `Sí` cuando se te pregunte si deseas instalar dependencias.
- `Sí` cuando se te pregunte si deseas inicializar un repositorio git.

Una vez hecho esto, puedes moverte al directorio del proyecto y comenzar la aplicación:

```bash
cd my-app
npm run dev
```

La aplicación debería estar ejecutándose en [localhost:4321](http://localhost:4321/). A continuación, ejecuta el siguiente comando para instalar la biblioteca necesaria para construir la aplicación:

```bash
npm install aws4fetch
```

La siguiente biblioteca está instalada:

- `aws4fetch`: Un cliente AWS para entornos que soportan fetch y SubtleCrypto.

## Integrar el adaptador de Cloudflare en tu proyecto Astro

Para generar URLs pre-firmadas para cada objeto dinámicamente, habilitarás la renderización del lado del servidor en tu proyecto Astro a través del adaptador de Cloudflare. Ejecuta el siguiente comando:

```bash
npx astro add cloudflare
```

Cuando se te pida, elige lo siguiente:

- `Y` cuando se te pregunte si deseas instalar las dependencias de Cloudflare.
- `Y` cuando se te pregunte si deseas hacer cambios en el archivo de configuración de Astro.

Has habilitado con éxito la renderización del lado del servidor en Astro.

Para asegurarte de que la salida sea desplegable en Cloudflare Workers, crea un archivo `wrangler.toml` en la raíz del proyecto con el siguiente código:

```toml
// Archivo: wrangler.toml

name = "amazon-s3-astro-workers"
main = "dist/_worker.js"
compatibility_date = "2025-04-01"
compatibility_flags = [ "nodejs_compat" ]

[assets]
directory="dist"
binding="ASSETS"

[vars]
AWS_KEY_ID=""
AWS_REGION_NAME=""
AWS_S3_BUCKET_NAME=""
AWS_SECRET_ACCESS_KEY=""
```

Después de eso, asegúrate de tener tanto un archivo `.env` como un archivo `wrangler.toml` con las variables definidas para que puedan ser accedidas durante `npm run dev` y cuando se despliegue en Cloudflare Workers respectivamente.

Además, actualiza el archivo `astro.config.mjs` con lo siguiente para poder acceder a estas variables en el código de manera programática:

```tsx
// Archivo: astro.config.mjs

// ... Importaciones existentes...
import { defineConfig, envField } from 'astro/config'

export default defineConfig({
  env: {
    schema: {
      AWS_KEY_ID: envField.string({ context: 'server', access: 'secret', optional: false }),
      AWS_REGION_NAME: envField.string({ context: 'server', access: 'secret', optional: false }),
      AWS_S3_BUCKET_NAME: envField.string({ context: 'server', access: 'secret', optional: false }),
      AWS_SECRET_ACCESS_KEY: envField.string({ context: 'server', access: 'secret', optional: false }),
    }
  }
  // adaptador
})
```

## Crear claves de acceso para usuarios IAM

- En la barra de navegación en la parte superior derecha de tu cuenta AWS, elige tu nombre de usuario y luego elige **Credenciales de seguridad**.

<img width="2990" height="1656" alt="Obtener URL de Despliegue en AWS Amplify" decoding="async" loading="lazy" class="mt-4 border rounded-sm bg-cover bg-center bg-no-repeat transform will-change-auto" src="https://ik.imagekit.io/vjeqenuhn/launchfast-website/astro-aws-fargate/Screenshot%202024-03-30%20at%202.58.38%E2%80%AFPM.png" />

- Desplázate hacia abajo hasta **Claves de acceso** y haz clic en **Crear clave de acceso**.

<img width="2996" height="742" alt="Obtener URL de Despliegue en AWS Amplify" decoding="async" loading="lazy" class="mt-4 border rounded-sm bg-cover bg-center bg-no-repeat transform will-change-auto" src="https://ik.imagekit.io/vjeqenuhn/launchfast-website/astro-aws-fargate/Screenshot%202024-03-30%20at%202.58.52%E2%80%AFPM.png" />

- Nuevamente, haz clic en **Crear clave de acceso**.

<img width="3024" height="1154" alt="Obtener URL de Despliegue en AWS Amplify" decoding="async" loading="lazy" class="mt-4 border rounded-sm bg-cover bg-center bg-no-repeat transform will-change-auto" src="https://ik.imagekit.io/vjeqenuhn/launchfast-website/astro-aws-fargate/Screenshot%202024-03-30%20at%202.59.01%E2%80%AFPM.png" />

- Copia la **Clave de acceso** y la **Clave de acceso secreta** generadas para usarlas como `AWS_ACCESS_KEY_ID` y `AWS_ACCESS_KEY_SECRET` respectivamente.

<img width="3024" height="1444" alt="Obtener URL de Despliegue en AWS Amplify" decoding="async" loading="lazy" class="mt-4 border rounded-sm bg-cover bg-center bg-no-repeat transform will-change-auto" src="https://ik.imagekit.io/vjeqenuhn/launchfast-website/astro-aws-fargate/Screenshot%202024-03-30%20at%202.59.10%E2%80%AFPM.png" />

## Configurar la Política del Bucket de Amazon S3

Para compartir de forma segura el acceso a un bucket de Amazon S3, necesitamos configurar una política de bucket que defina explícitamente quién puede acceder al bucket y qué acciones se les permite realizar. En este caso, queremos otorgar permiso a una cuenta de AWS específica para cargar (PutObject) y descargar (GetObject) archivos de un bucket S3 en particular. Esto es útil cuando deseas que otra cuenta, como un socio, un entorno separado o una integración de terceros, interactúe programáticamente con tu bucket sin hacer públicos los contenidos.

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AuthenticatedReadUploadObjects",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<user-id>:root"
            },
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::<bucket-name>/*"
        }
    ]
}
```

En la configuración anterior, has restringido las operaciones Get y Put al usuario con el ID `<user-id>` y para el bucket `<bucket-name>`.

## Configurar CORS de Amazon S3

Dado que usarás URLs pre-firmadas para cargar desde el lado del cliente (navegador) a Amazon S3, se vuelve necesario permitir que Amazon S3 sepa que debe aceptar cargas desde cualquier lugar de la web.

**NOTA:** Puedes editar los `AllowedOrigins` para limitar a dominios específicos, pero en este caso nos preocupamos menos por el nivel de acceso ya que ya estamos aplicando autenticación IAM al generar las URLs pre-firmadas.

```json
[
  {
    "AllowedHeaders": [
      "*"
    ],
    "AllowedMethods": [
      "GET",
      "PUT" 
    ],
    "AllowedOrigins": [
      "*"
    ],
    "ExposeHeaders": [],
    "MaxAgeSeconds": 9000
  }
]
```

## Generar las URLs pre-firmadas

### 1. Acceder a las Variables de Entorno

El primer paso es acceder a las variables de entorno necesarias durante el tiempo de ejecución para crear un Cliente AWS a través de aws4fetch. Desde [Astro 5.6 en adelante](https://astro.build/blog/astro-560/), la forma en que deseas acceder a las variables de entorno en tu código es utilizando la función `getSecret` de `astro:env/server` para mantener las cosas agnósticas del proveedor. Esto es crucial para almacenar información sensible de manera segura sin codificarla directamente en tu aplicación. Recuperarás las siguientes variables:

- ID de Clave de Acceso de AWS
- Nombre de la Región de AWS
- Nombre del Bucket de Amazon S3
- Clave de Acceso Secreta de AWS

```typescript
// Archivo: src/storage/s3.ts

import { getSecret } from 'astro:env/server'

const accessKeyId = getSecret('AWS_KEY_ID')
const s3RegionName = getSecret('AWS_REGION_NAME')
const s3BucketName = getSecret('AWS_S3_BUCKET_NAME')
const secretAccessKey = getSecret('AWS_SECRET_ACCESS_KEY')
```

### 2. Definir el Cliente AWS

A continuación, definirás la función `defineAws4Fetch` que crea una instancia de cliente AWS usando las variables de entorno requeridas. Esta función verifica si las credenciales de AWS necesarias están configuradas y devuelve una nueva instancia de `AwsClient` configurada para S3.

```typescript
// Archivo: src/storage/s3.ts
import { AwsClient } from 'aws4fetch'

// ...Código Existente...

async function defineAws4Fetch(): Promise<AwsClient> {
  if (!accessKeyId || !secretAccessKey) {
    throw new Error(`Las variables de entorno AWS_KEY_ID O AWS_SECRET_ACCESS_KEY no están configuradas.`)
  }
  return new AwsClient({
    service: 's3',
    accessKeyId,
    secretAccessKey,
    region: s3RegionName,
  })
}
```

### 3. Determinar URLs de S3

Necesitarás generar URLs únicas para cada carga de archivo a Amazon S3. La función `getS3URL` a continuación se encarga de construir la URL correcta basada en el nombre del archivo, el nombre del bucket y la región.

```typescript
// Archivo: src/storage/s3.ts

// ...Código Existente...

function getS3URL({ Key }: { Key: string }) {
  if (!s3BucketName) {
    throw new Error(`La variable de entorno AWS_S3_BUCKET_NAME no está configurada.`)
  }
  return new URL(`/${Key}`, `https://${s3BucketName}.s3.${s3RegionName}.amazonaws.com`)
}
```

### 4. URL pre-firmada para GET un Objeto S3 (recuperar)

La función `getS3ObjectURL` a continuación recupera una URL pre-firmada de un objeto de Amazon S3. Genera una solicitud firmada que te permite acceder al archivo de manera segura.

```typescript
// Archivo: src/storage/s3.ts

// ...Código Existente...

export async function getS3ObjectURL(Key: string) {
  try {
    const endpointUrl = getS3URL({ Key })
    endpointUrl.searchParams.set('X-Amz-Expires', '3600')
    const client = await defineAws4Fetch()
    const signedRequest = await client.sign(new Request(endpointUrl), { aws: { signQuery: true } })
    return signedRequest.url
  } catch (e: any) {
    const tmp = e.message || e.toString()
    console.log(tmp)
    return
  }
}
```

### 5. URL pre-firmada para PUT un Objeto S3 (cargar)

La función `uploadS3ObjectURL` a continuación es responsable de generar una URL pre-firmada para cargar un archivo a Amazon S3. Sigue una estructura similar a la función `getS3ObjectURL`, generando una URL firmada que te permite cargar archivos de manera segura.

```typescript
// Archivo: src/storage/s3.ts

// ...Código Existente...

export async function uploadS3ObjectURL(file: { name: string; type: string }) {
  try {
    const Key = file.name
    const endpointUrl = getS3URL({ Key })
    endpointUrl.searchParams.set('X-Amz-Expires', '3600')
    const client = await defineAws4Fetch()
    const signedRequest = await client.sign(new Request(endpointUrl, { method: 'PUT', headers: { 'Content-Type': file.type } }), { method: 'PUT', aws: { signQuery: true } })
    return signedRequest.url
  } catch (e: any) {
    const tmp = e.message || e.toString()
    console.log(tmp)
    return
  }
}
```

### 6. Crear un Endpoint del Servidor (una Ruta API) en Astro

```tsx
// Archivo: src/pages/api/storage.ts

import type { APIContext } from 'astro'
import { getS3ObjectURL, uploadS3ObjectURL } from '../../storage/s3'

// Define una función asincrónica llamada GET que acepta un objeto de solicitud.
export async function GET({ request }: APIContext) {
  // Extrae el parámetro 'file' de la URL de la solicitud.
  const url = new URL(request.url)
  const file = url.searchParams.get('file')
  // Verifica si el parámetro 'file' existe en la URL.
  if (file) {
    try {
      const filePublicURL = await getS3ObjectURL(file)
      // Devuelve una respuesta con la URL pública de la imagen y un código de estado 200.
      return new Response(filePublicURL)
    } catch (error: any) {
      // Si ocurre un error, registra el mensaje de error y devuelve una respuesta con un código de estado 500.
      const message = error.message || error.toString()
      console.log(message)
      return new Response(message, { status: 500 })
    }
  }
  // Si el parámetro 'file' no se encuentra en la URL, devuelve una respuesta con un código de estado 400.
  return new Response('Solicitud Inválida.', { status: 400 })
}

export async function POST({ request }: APIContext) {
    // Extrae el parámetro 'file' de la URL de la solicitud.
    const url = new URL(request.url)
    const type = url.searchParams.get('type')
    const name = url.searchParams.get('name')
    if (!type || !name) return new Response('Solicitud Inválida.', {status:400})
    try {
      // Genera una URL accesible para el archivo cargado
      // Usa esta URL para realizar un GET a este endpoint con el parámetro de consulta file valorado como a continuación
      const publicUploadUrl = await uploadS3ObjectURL({ type, name })
      // Devuelve una respuesta de éxito con un mensaje
      return new Response(publicUploadUrl)
    } catch (error: any) {
      // Si hubo un error durante el proceso de carga, devuelve una respuesta 403 con el mensaje de error
      const message = error.message || error.toString()
      console.log(message)
      return new Response(message, { status: 500 })
    }
}
```

## Desplegar en Cloudflare Workers

Para hacer que tu aplicación sea desplegable en Cloudflare Workers, crea un archivo llamado `.assetsignore` en el directorio `public` con el siguiente contenido:

```
_routes.json
_worker.js
```

A continuación, necesitarás usar la CLI de Wrangler para desplegar tu aplicación en Cloudflare Workers. Ejecuta el siguiente comando para desplegar:

```bash
npm run build && npx wrangler@latest deploy
```

## Referencias

- [Repositorio de GitHub](https://github.com/rishi-raj-jain/amazon-s3-astro-workers)

### Conclusión

En este post de blog, aprendiste cómo integrar Amazon S3 con Astro y Cloudflare Workers para cargas y recuperaciones de archivos. Siguiendo los pasos de implementación, puedes cargar y recuperar archivos de Amazon S3 de manera segura, asegurando que tu aplicación web tenga una solución de almacenamiento robusta y flexible.

Si deseas explorar secciones específicas con más detalle, ampliar ciertos conceptos o cubrir temas adicionales relacionados, por favor házmelo saber, ¡y estaré encantado de ayudar!
