1) Conceptos clave
- Signed URL: enlace con firma HMAC y expiración que autoriza descargas temporales.
- Signed cookies: autorización por cookie para múltiples recursos sin variar la URL.
- Origen: S3/GCS/Azure o tu servidor. Nunca debe ser público si sirves contenido protegido.
- CDN: distribuye y cachea; aplica políticas de firma, expiración y headers.
2) Estrategias de firmado (elige según caso)
Estrategia | Ventajas | Cuándo usarla |
---|---|---|
Query string (p. ej., ?Expires=&Signature=&KeyId= ) |
Simple, fácil de depurar | Descargas únicas, enlaces compartidos |
Path-based (firma el path) | URLs limpias, se puede cachear por patrón | CDN con reglas por ruta, catálogos |
Signed cookies | Alta tasa de aciertos en caché (misma URL), ideal para galerías | Apps autenticadas con muchas imágenes por sesión |
3) Integración con WordPress (Media Library)
Genera la firma en servidor y reescribe las URLs de los adjuntos en tiempo de render. Usa hooks para no tocar contenido histórico.
Ejemplo: firmar S3/CloudFront con PHP
<?php
// functions.php
use Aws\CloudFront\CloudFrontClient;
function cdn_signed_url($path) {
$cf = new CloudFrontClient([
'version' => 'latest',
'region' => 'us-east-1'
]);
$expires = time() + 300; // 5 minutos
// Cambia por tu distribucion y clave (Key Pair ID + clave privada)
return $cf->getSignedUrl([
'url' => 'https://cdn.tudominio.com' . $path,
'expires' => $expires,
'key_pair_id' => getenv('CF_KEY_ID'),
'private_key' => file_get_contents(WP_CONTENT_DIR . '/keys/cf_private_key.pem')
]);
}
// Reescribe URLs de adjuntos
add_filter('wp_get_attachment_url', function($url, $post_id){
$path = parse_url($url, PHP_URL_PATH);
return cdn_signed_url($path);
}, 10, 2);
// Opcional: srcset
add_filter('wp_calculate_image_srcset', function($sources){
foreach($sources as &$s){ $s['url'] = cdn_signed_url(parse_url($s['url'], PHP_URL_PATH)); }
return $sources;
}, 10, 1);
?>
Ejemplo: firmar URLs tipo query (genérico HMAC)
<?php
function sign_url_query($baseUrl, $secret, $ttl=300) {
$exp = time() + $ttl;
$data = parse_url($baseUrl, PHP_URL_PATH) . $exp;
$sig = hash_hmac('sha256', $data, $secret);
$sep = strpos($baseUrl,'?')!==false ? '&' : '?';
return $baseUrl . $sep . 'exp=' . $exp . '&sig=' . $sig;
}
?>
4) Caché y rendimiento en CDN
- Evita busting innecesario: las firmas distintas por request bajan el hit-ratio. Para catálogos grandes considera signed cookies.
- TTL: corto en la firma (2–15 min), largo en el archivo estático del origen (1 año con
immutable
). - Vary: no hagas
Vary: *
por User-Agent salvo que uses device-aware resizing. - Formatos modernos: sirve WebP/AVIF; conserva originales para compatibilidad.
- Responsive: usa
srcset
y los tamaños registrados en WP para reducir bytes. - Lazy loading: mantiene LCP bajo y menos transferencias.
- Optimización en build: plugins de compresión sin romper el esquema de firmado (evita que re-escriban la URL final).
Headers recomendados
# Nginx como proxy delante del origen
location /media/ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; upgrade-insecure-requests";
add_header X-Content-Type-Options "nosniff";
}
5) Seguridad (evita filtraciones)
- Nunca expongas la clave privada en el cliente. Firma en servidor o en una Function/Edge.
- Rotación de claves y grace period para evitar caídas durante el cambio.
- Reglas anti-hotlinking y límites de tasa por IP.
- Lista de dominios permitidos para referer o token de sesión.
- Evita indexación de URLs firmadas (no enlaces permanentes; añade
noindex
en páginas privadas). - Logs: habilita acceso a logs del CDN para auditoría (IP, UA, referer, status).
6) Flujo recomendado
- Sube originales a almacenamiento privado (S3/GCS/Azure).
- Genera tamaños de WP (thumbnail, medium, large, etc.).
- Sirve a través de CDN con origin private.
- Autoriza con signed cookies para vistas autenticadas; usa URLs firmadas en descargas puntuales.
- TTL corto en firma; invalida selectivamente cuando revocas acceso.
7) Ejemplos por proveedor
CloudFront: política de cookie firmada
// Set-Cookie desde PHP (sesión 15 min)
setcookie('CloudFront-Policy', $policy, 0, '/', '.tudominio.com', true, true);
setcookie('CloudFront-Signature', $signature, 0, '/', '.tudominio.com', true, true);
setcookie('CloudFront-Key-Pair-Id', getenv('CF_KEY_ID'), 0, '/', '.tudominio.com', true, true);
GCS o proxy con firma HMAC
// Genera firma HMAC y redirige a /media/path?exp=...&sig=...
header('Location: ' . sign_url_query('/media/'.$_GET['file'], getenv('MEDIA_SECRET')));
exit;
8) Errores comunes
- Firmar cada imagen con TTL de segundos → cache miss y coste alto.
- Guardar la URL firmada en la base de datos (se vuelve obsoleta). Mejor firmar al vuelo.
- Hacer públicas las rutas del bucket/origen.
- Transformaciones on-the-fly pesadas en PHP sin caché.
- Plugins de optimización que reescriben URLs y rompen la firma.
9) FAQ
¿Signed URLs o signed cookies? Cookies para sitios con usuarios autenticados y muchas imágenes; URLs para enlaces de descarga o recursos puntuales.
¿Puedo cachear imágenes firmadas? Sí, pero el cache hit baja si cada URL es única. Las cookies firmadas preservan la misma URL y mejoran el hit-ratio.
¿Qué TTL uso? Entre 5 y 15 minutos suele equilibrar seguridad y rendimiento; ajusta según el riesgo.
10) Checklist de implementación
- Origen privado, acceso solo desde CDN.
- Firma generada en servidor/edge; secretos fuera del cliente.
- Signed cookies para galerías; URLs firmadas para descargas.
- TTL corto en firma; 1 año en assets base con
immutable
. srcset
, WebP/AVIF, lazy loading activados.- Reglas anti-hotlinking y rate limiting.
- Rotación de claves y monitorización.