Integración de Bucket privado de imagenes en AWS S3 con Django

By valbornoz July 20, 2026, 3:36 p.m.

1. Crear el Bucket

En AWS:

S3
→ Create Bucket

Ejemplo

Nombre:
mi-empresa-archivos

Region:  # Elegir acorde con la region de tu servidor de backend
us-east-1

Lo importante es:

Block Public Access

Dejar todo activado.

Block all public access

Eso hace que absolutamente nadie pueda acceder mediante

https://bucket.s3.amazonaws.com/archivo.pdf

aunque conozca el nombre.

Politicas de bucket AWS S3

2. IAM

Nunca se usan las credenciales de la cuenta root.

Se crea un usuario IAM.

IAM
Create User
django-production

Después una política.

Ejemplo:

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Action":[
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource":"arn:aws:s3:::mi-empresa-archivos/*"
    },
    {
      "Effect":"Allow",
      "Action":[
        "s3:ListBucket"
      ],
      "Resource":"arn:aws:s3:::mi-empresa-archivos"
    }
  ]
}

Después se generan

Access Key

Secret Access Key

3. Variables de entorno

Normalmente se guardan en tu settings.py o un archivo como .env o settings_server.py

AWS_ACCESS_KEY_ID=AKIAxxxxxxxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxx
AWS_STORAGE_BUCKET_NAME=mi-empresa-archivos
AWS_S3_REGION_NAME=us-east-1
AWS_S3_SIGNATURE_VERSION=s3v4
AWS_QUERYSTRING_EXPIRE=300

donde

300 segundos = 5 minutos

4. Instalar librerías

pip install boto3

o

pip install django-storages boto3

4. Instalar librerías

pip install boto3

o

pip install django-storages boto3

5. settings.py

Agregar

INSTALLED_APPS += [
    "storages",
]

Después

AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = os.getenv("AWS_STORAGE_BUCKET_NAME")
AWS_S3_REGION_NAME = os.getenv("AWS_S3_REGION_NAME")
AWS_S3_SIGNATURE_VERSION = "s3v4"
AWS_QUERYSTRING_AUTH = True
AWS_QUERYSTRING_EXPIRE = 300
AWS_DEFAULT_ACL = None

Muy importante:

AWS_DEFAULT_ACL = None

Desde hace varios años Amazon recomienda no usar ACLs.

Storage

DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"

Con eso todos los

FileField
ImageField

guardarán archivos en S3.

6. Modelo

class Documento(models.Model):

    nombre = models.CharField(max_length=100)

    archivo = models.FileField(
        upload_to="documentos/"
    )

Cuando haces

doc.archivo.save(...)

el archivo ya no queda en

MEDIA_ROOT

sino en

S3

por ejemplo

documentos/factura.pdf

7. Subir archivos

Ejemplo

documento = Documento()

documento.archivo.save(
    "contrato.pdf",
    ContentFile(pdf)
)

Internamente django-storages hace

PutObject

contra S3.

8. Obtener URL

Como el bucket es privado,

documento.archivo.url

no devuelve

https://bucket.s3.amazonaws.com/archivo.pdf

sino algo parecido a

https://mi-empresa-archivos.s3.amazonaws.com/documentos/contrato.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=...
&X-Amz-Date=...
&X-Amz-Expires=300
&X-Amz-Signature=...

Es una URL firmada.

Después de 5 minutos

ExpiredToken

9. Generar URL manualmente con boto3

Muchas empresas prefieren hacerlo manualmente.

import boto3
client = boto3.client(
    "s3",
    aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
    aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
    region_name=settings.AWS_S3_REGION_NAME
)

url = client.generate_presigned_url(
    ClientMethod="get_object",
    Params={
        "Bucket": settings.AWS_STORAGE_BUCKET_NAME,
        "Key": "documentos/factura.pdf"
    },
    ExpiresIn=300
)

Obtienes exactamente la misma URL temporal.

10. Subida directa desde Django

Flujo

Usuario

↓

POST /upload/

↓

Django

↓

S3 PutObject

↓

Guardar ruta en BD
archivo = request.FILES["archivo"]
modelo.archivo = archivo
modelo.save()

No necesitas escribir código de boto3 porque django-storages se encarga del proceso.

11. Variables importantes

Variable Función
AWS_ACCESS_KEY_ID Llave pública del usuario IAM.
AWS_SECRET_ACCESS_KEY Llave secreta asociada.
AWS_STORAGE_BUCKET_NAME Nombre del bucket donde se almacenan los archivos.
AWS_S3_REGION_NAME Región del bucket (por ejemplo, us-east-1).
AWS_S3_SIGNATURE_VERSION Versión de firma usada para las URLs (s3v4 es la recomendada).
AWS_QUERYSTRING_AUTH Si es True, genera URLs firmadas para acceso privado.
AWS_QUERYSTRING_EXPIRE Tiempo de vida de la URL firmada en segundos.
AWS_DEFAULT_ACL Debe ser None para seguir las recomendaciones actuales de AWS.
DEFAULT_FILE_STORAGE Indica a Django que use S3 como backend de almacenamiento.