Servir un site depuis R2 : trois pièges et un 404 qui ment
Le plan : servir cette vitrine statique depuis un bucket Cloudflare R2, via l’image
nginxinc/nginx-s3-gateway (un nginx qui authentifie + cache un bucket S3-compatible),
derrière l’ingress du cluster. Sur le papier, dix minutes. En vrai, trois pièges —
dont un 404 qui ment effrontément.
Piège 1 — zone "s3_backends" is too small
Premier déploiement : crash-loop immédiat.
nginx: [emerg] zone “s3_backends” is too small
Le gateway déclare une zone de mémoire partagée zone s3_backends 64k. Sur le worker
(Raspberry Pi 5, arm64), la taille de page mémoire est de 16 Kio — et l’allocateur
slab de nginx refuse une zone qui ne tient pas sur assez de pages. 64k = 4 pages : trop
peu. Sur des pages 4 Kio classiques (x86), 64k = 16 pages, ça passe ; d’où le bug
spécifique arm64.
Correctif : on monte un template upstreams.conf patché (zone 512k) en config Swarm.
Piège 2 — un resolver qui part en IPv6
Zone réglée, nouveau symptôme : 404 sur tout, et dans les logs :
connect() to [2606:4700:…]:443 failed (101: Network is unreachable)
Le cluster n’a pas d’IPv6 sortant, mais le resolver de nginx récupère l’enregistrement
AAAA de R2 et tente l’IPv6 → injoignable. Une ligne suffit :
resolver 127.0.0.11 ipv6=off;
Piège 3 — le 404 qui ment (en fait c’est la signature)
Toujours 404. Mais cette fois aucune erreur réseau : nginx joint R2 en IPv4, et R2
répond 404. L’objet existe pourtant (aws s3 ls le voit). Un 404, normalement, c’est
“clé absente”… sauf qu’ici, ce n’en est pas un.
DEBUG=true sur le gateway, et on lit la requête canonique SigV4 envoyée à R2 :
host:5dbfd0…r2.cloudflarestorage.com:443 ← le :443 est signé
Voilà le coupable. La signature AWS v4 inclut le header Host, et le gateway y colle
:443. R2 calcule la signature sans le port (comme aws-cli, qui marche), les deux ne
correspondent pas — et R2 répond… 404, pas 403. Trompeur au possible.
La cause : le S3_STYLE. Le gateway expose trois styles d’adressage, et un seul
construit un Host sans le port :
S3_STYLE | Header Host signé | R2 |
|---|---|---|
virtual-v2 | bucket.endpoint:443 | ❌ |
path | endpoint:443 | ❌ |
virtual (legacy) | bucket.endpoint | ✅ |
Correctif : S3_STYLE=virtual. Connexion à l’endpoint compte (qui a bien des
enregistrements A), Host sans port, signature alignée sur aws-cli. 200.
La leçon
Un
404de R2 ne veut pas toujours dire “objet manquant”. Ici, c’était une signature SigV4 invalide (un:443de trop dans leHost). Quand le code HTTP n’a pas de sens, activez le debug et lisez la requête canonique : la vérité est dans ce que vous signez, pas dans ce que vous croyez envoyer.
Trois pièges, trois lignes de config. Et un site servi depuis un bucket, derrière le même ingress que le reste du cluster.
📬 La newsletter homelab
Les nouveaux articles + retours d'expérience self-hosting, sans spam. Désinscription en un clic.