# Audit & optimisation Nginx — dash.seoinsight.app

Serveur : `sgwsnlcupaqyxdqux@163.172.71.169` · Nginx **1.28.3** · Ubuntu 26.04 · sans CDN.

## Verdict

Tes **vhosts sont excellents** (catch-all `444` + `ssl_reject_handshake`, offload assets statiques, map WebSocket, timeouts Server Actions, redirects propres). Les manques sont dans le **`nginx.conf` stock Ubuntu** (jamais tuné) + quelques optimisations vhost.

## Findings

| # | Prio | Constat | Correctif | Fichier |
|---|---|---|---|---|
| 1 | 🔴 | HTTP/2 désactivé (`# http2 on;`) | `http2 on;` | vhost |
| 2 | 🔴 | Pas de HSTS (site HTTPS-only) | `Strict-Transport-Security` 1 an | vhost |
| 3 | 🔴 | `worker_connections 768` (défaut bas) | 4096 + `worker_rlimit_nofile 65535` + `multi_accept` | nginx.conf |
| 4 | 🟠 | `server_tokens build` fuite la version | `server_tokens off` | nginx.conf |
| 5 | 🟠 | Pas de `limit_req` → Node encaisse les floods, jail f2b inactif | zones + `limit_req`/`limit_conn` (429) | conf.d + vhost |
| 6 | 🟡 | Proxy sans keepalive upstream | `upstream { keepalive 32 }` + map `""` | vhost + conf.d |
| 7 | 🟡 | Pas de cache session TLS global | `ssl_session_cache` | nginx.conf |
| 8 | 🟡 | gzip minimal au niveau http | gzip_types + `gzip_vary` | nginx.conf |

> **Détail keepalive (#6)** : ton commentaire actuel dit que `Connection: close` « préserve le keepalive » — c'est l'inverse. `close` ferme la connexion Node à chaque requête. La version optimisée passe la map à `""` (vide) hors WebSocket → les connexions vers Node sont réellement réutilisées. WebSocket reste intact (`upgrade` pendant le handshake).

## Fichiers produits (`deploy/nginx/`)

```
nginx.conf                              → /etc/nginx/nginx.conf
conf.d/10-ratelimit.conf                → /etc/nginx/conf.d/10-ratelimit.conf     (NOUVEAU)
conf.d/10-upgrade-map.conf              → /etc/nginx/conf.d/10-upgrade-map.conf    (remplace : '' -> "")
sites-available/dash.seoinsight.app     → /etc/nginx/sites-available/dash.seoinsight.app
```

Le catch-all (`00-catch-all`) est déjà parfait → **inchangé**.

---

## Déploiement

```fish
set SRV sgwsnlcupaqyxdqux@163.172.71.169
set N   "/Users/humperdoo/Documents/seo insight/serveur/fail2ban/deploy/nginx"

# 1. Envoi dans /tmp
scp "$N/nginx.conf"                          $SRV:/tmp/nginx.conf
scp "$N/conf.d/10-ratelimit.conf"            $SRV:/tmp/10-ratelimit.conf
scp "$N/conf.d/10-upgrade-map.conf"          $SRV:/tmp/10-upgrade-map.conf
scp "$N/sites-available/dash.seoinsight.app" $SRV:/tmp/dash.seoinsight.app

# 2. Backup + mise en place + TEST + reload (rien n'est appliqué si nginx -t échoue)
ssh -tt $SRV '
  set -e
  ts=$(date +%Y%m%d-%H%M%S)
  sudo cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$ts
  sudo cp -a /etc/nginx/conf.d/10-upgrade-map.conf /etc/nginx/conf.d/10-upgrade-map.conf.bak.$ts
  sudo cp -a /etc/nginx/sites-available/dash.seoinsight.app /etc/nginx/sites-available/dash.seoinsight.app.bak.$ts

  sudo install -m 644 -o root -g root /tmp/nginx.conf           /etc/nginx/nginx.conf
  sudo install -m 644 -o root -g root /tmp/10-ratelimit.conf    /etc/nginx/conf.d/10-ratelimit.conf
  sudo install -m 644 -o root -g root /tmp/10-upgrade-map.conf  /etc/nginx/conf.d/10-upgrade-map.conf
  sudo install -m 644 -o root -g root /tmp/dash.seoinsight.app  /etc/nginx/sites-available/dash.seoinsight.app
  rm -f /tmp/nginx.conf /tmp/10-ratelimit.conf /tmp/10-upgrade-map.conf /tmp/dash.seoinsight.app

  echo "===== nginx -t ====="
  sudo nginx -t
'

# 3. Si "syntax is ok" -> recharge sans coupure
ssh -tt $SRV 'sudo systemctl reload nginx && echo "reload OK"'
```

## Vérification

```fish
ssh -tt $SRV '
  echo "== HTTP/2 + HSTS =="; curl -sI https://dash.seoinsight.app | grep -iE "^HTTP|strict-transport|server:"
  echo "== worker_connections =="; grep worker_connections /etc/nginx/nginx.conf
'
```
Attendu : `HTTP/2 200`, une ligne `strict-transport-security`, et pas de version dans `Server:` (juste `nginx`).

## Rollback

```fish
ssh -tt $SRV '
  ts=$(ls -t /etc/nginx/nginx.conf.bak.* | head -1 | sed "s/.*bak\.//")
  sudo cp -a /etc/nginx/nginx.conf.bak.$ts            /etc/nginx/nginx.conf
  sudo cp -a /etc/nginx/conf.d/10-upgrade-map.conf.bak.$ts /etc/nginx/conf.d/10-upgrade-map.conf
  sudo cp -a /etc/nginx/sites-available/dash.seoinsight.app.bak.$ts /etc/nginx/sites-available/dash.seoinsight.app
  sudo rm -f /etc/nginx/conf.d/10-ratelimit.conf
  sudo nginx -t && sudo systemctl reload nginx
'
```

---

## Lien avec fail2ban

`deploy/jail.local` a été mis à jour : le jail **`nginx-limit-req` est maintenant activé** (il lit les `limiting requests` dans `error.log` produits par le `limit_req` ci-dessus). Déploie Nginx **avant ou en même temps** que le fail2ban durci (voir `DEPLOY.md`).

## Points à surveiller après déploiement

1. **Stripe webhooks** : si un burst se fait throttler (HTTP 429 dans les logs Stripe), ajoute un `location = /api/stripe/webhook { ... }` (chemin exact à confirmer côté app) **sans** `limit_req`, ou augmente `burst`.
2. **Uploads CSV 60 Mo** : non impactés par `limit_req` (1 grosse requête ≠ débit de requêtes), `client_max_body_size 60M` conservé.
3. **HSTS** : une fois envoyé, les navigateurs forceront HTTPS 1 an. Assure-toi de ne jamais vouloir revenir en HTTP simple sur ce sous-domaine.
