non testé Configuration non testée

Cette configuration est fournie comme point de départ et transposée depuis la configuration Apache réellement utilisée par Aetheus. Elle n'a pas été exécutée sur un déploiement réel : traitez chaque valeur comme une piste à vérifier, pas comme une recette éprouvée.

nginx

Une configuration nginx transposée depuis la configuration Apache utilisée par Aetheus en production. Mêmes deux noms d'hôte, mêmes deux règles sur le frontend, même exigence WebSocket sur l'API.

La table de mise à niveau de connexion

Le relayage WebSocket sous nginx nécessite une table associant l'en-tête Upgrade entrant à l'en-tête Connection sortant. Elle appartient au bloc http : placez-la dans /etc/nginx/conf.d/upgrade.conf et déclarez-la une seule fois.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

L'oublier est de loin la première cause de sortie en direct qui cesse de se rafraîchir alors que tout le reste semble fonctionner.

Hôte applicatif

Il sert le frontend, soit en relayant vers le conteneur, soit en servant le wwwroot publié depuis le disque. Les deux variantes sont données.

server {
    listen 443 ssl;
    http2 on;
    server_name app.example.com;

    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Une application Blazor WebAssembly s'amorce depuis index.html et _framework/. Les mettre en
    # cache fait qu'un navigateur continue d'exécuter la version précédente après une mise à jour.
    location ~ ^/(index\.html|service-worker(\.published)?\.js|_framework/|_content/Radzen\.Blazor/) {
        add_header Cache-Control "no-cache, no-store, must-revalidate" always;
        add_header Pragma "no-cache" always;
        expires -1;

        proxy_pass http://127.0.0.1:10025;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }

    location / {
        proxy_pass http://127.0.0.1:10025;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}

Servir les fichiers directement plutôt que les relayer

Si vous avez publié le frontend sur disque, remplacez les deux blocs location par :

    root /var/www/aetheus-app;
    index index.html;

    location ~ ^/(index\.html|service-worker(\.published)?\.js|_framework/|_content/Radzen\.Blazor/) {
        add_header Cache-Control "no-cache, no-store, must-revalidate" always;
        expires -1;
        try_files $uri =404;
    }

    location / {
        # Routage côté client : les chemins inconnus doivent atteindre index.html, pas un 404.
        try_files $uri $uri/ /index.html;
    }

Vérifiez que votre build associe bien .wasm dans mime.types ; les versions récentes de nginx le font. Sinon, les fichiers du runtime sont servis en application/octet-stream et l'application ne s'amorce pas.

Hôte API

Il sert le backend. Les trois lignes de mise à niveau sont ce qui permet à la sortie en direct de fonctionner.

server {
    listen 443 ssl;
    http2 on;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;

    # Les téléversements et téléchargements d'artefacts passent par cet hôte ; la limite de 1m par défaut est bien trop basse.
    client_max_body_size 512m;

    location / {
        proxy_pass http://127.0.0.1:10026;
        proxy_http_version 1.1;

        proxy_set_header Upgrade    $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;

        # La sortie d'exécution en flux ne doit pas être coupée en cours de route.
        proxy_read_timeout 300s;
        proxy_buffering off;
    }
}

Rediriger le HTTP en clair

server {
    listen 80;
    server_name app.example.com api.example.com;
    return 301 https://$host$request_uri;
}

Appliquer

sudo nginx -t
sudo systemctl reload nginx

Lancez toujours nginx -t d'abord : un rechargement avec une configuration cassée coupe le site au lieu d'être ignoré.

Vérifier

curl -sf https://api.example.com/health/ready
curl -sI https://app.example.com/ | grep -i cache-control

Ouvrez ensuite l'hôte applicatif et connectez-vous. Une page qui se charge alors que chaque appel échoue désigne le ApiBaseUrl du frontend ou le Cors__Origins__0 du backend, pas nginx.