# Plano 18 — Otimização de resposta e hardening (herdado do v6)

> **Milestone:** M1→M4 (transversal) · **Depende de:** 04, 06, 08, 14 · **Habilita:** produção
> **Objetivo:** integrar o que o `v6-hardening` provou valer — **de forma nativa e simples** —
> descartando o que era contorno do legado (L5.5/PHP 7.2/cPanel/6 modelos/CDN).
> **Fontes:** `v6-hardening/` (épicos + `wiki/infra-update/*`), `minimal-web-security-gate/`,
> `pesquisa-infra-cdn/`.

---

## 1. Decisão por item (adotar / adaptar / descartar)

| Item do v6 | Camada | Decisão v7 | Onde |
|---|---|---|---|
| Cache-Control público + `Vary: Accept-Encoding` | L1.1 | **Adotar** | plano 18 §2 (ADR-022) |
| ETag + `304` | L1.2 | **Adotar** | plano 18 §2 |
| CSRF fora do HTML cacheável (L1.0) | L1.0 | **Adotar** (form pages `private`) | plano 18 §2 |
| FPC de origem | L2.3 | **Adaptar/opcional** (Valkey, sem pacote) | plano 18 §3 |
| Cache de layout/consultas + eager loading | L2.4 | **Adotar** (já em 08/14) | planos 08/14 |
| `config:cache`/`route:cache`/`optimize` | L2.1 | **Adotar** (já em ADR-008) | ADR-008 |
| OPcache `validate_timestamps=0` + reset no deploy | L0.1 | **Adotar** (já em ADR-008) | ADR-008 |
| Compressão gzip/brotli server-side | L0.2 | **Adotar** | plano 04 |
| `Cache-Control: immutable` em assets versionados | L0.3 | **Adotar** (Vite 8 hash) | plano 04 |
| Vendorização (zero CDN) | L4.2 | **Já decidido** | ADR-009/010 |
| Build de produção com hash/minify | L4.1 | **Já decidido** (Vite 8) | ADR-008/010 |
| WebP/AVIF + `<picture>` responsivo | L5 | **Adaptar** (componente Blade, GD/Imagick nativo) | plano 08/14 |
| HTML minify em runtime | L6.1 | **Descartar** (gzip/brotli basta) | — |
| CSP nonce + hash no legado | SEC-1 | **Descartar o contorno; adotar o alvo** | ADR-020 + ADR-022 |
| Headers estáticos (CORP/COOP/HSTS/referrer/permissions/x-frame/nosniff) | SEC-2..5 | **Adotar** (já em ADR-020) | ADR-020 |
| Cookie `Secure`/`SameSite`/`use_strict_mode` | SEC-3 | **Adotar** (já em ADR-020) | ADR-020 |
| Redirect 301 HTTP→HTTPS (aware `X-Forwarded-Proto`) | SEC-5 | **Adotar** | plano 04 |
| `TrustProxies` (IP real) | — | **Adotar** | plano 04 (ADR-021) |
| Server-Timing + RUM + Lighthouse budget | Fase Final | **Adotar** | plano 06/11 |
| Speculation Rules / `content-visibility` / font preload | Fase Final | **Adaptar** | plano 09/14 |
| Borda Multi-CDN (A2/A3/A4) | externo | **Backlog** (externo, não MVP) | `wiki/backlog/cdn-borda-multicdn.md` |
| Minify/critical-CSS dos 6 modelos | L7 | **Descartar** (não existe no v7) | — |

## 2. Cache de resposta, ETag e CSRF (ADR-022)

- [x] Middleware fino de **`Cache-Control`** para GET público de conteúdo (portal), com
      `stale-while-revalidate`; `Vary: Accept-Encoding, Cookie` (`Cookie` cobre a11y no SSR — ADR-016;
      nunca `User-Agent`). Global `StripPublicCookies` remove o `Set-Cookie` anônimo.
- [x] **`ETag` + `304`** (corpo determinístico) no mesmo fluxo. `ETag` por hash do corpo **só no
      `GET`**: o `Response::prepare()` do Symfony remove o corpo no `HEAD` antes dos middlewares.
- [x] **Form pages `private, no-store`** (rota `cache.private` + detecção de `<form method="post">`);
      nenhum token CSRF em HTML público (ADR-022).
- [x] Testes: `ResponseCacheTest` (`ETag` estável, `304`, form `private`, sem `Set-Cookie`) +
      e2e `14`.

## 3. FPC opcional (Valkey)

- [ ] (Opcional) middleware de **FPC** para GET público em Valkey + invalidação por evento de
      publicação (Scheduler/Observer). Sem pacote. Descartar se o ganho não justificar.

## 4. Assets, compressão e imagens

- [x] **brotli/gzip** no servidor + **`immutable`** nos assets do Vite (plano 04 — `@viteAssets` no
      Caddyfile; o build gera `.br`/`.gz` determinísticos).
- [ ] **`TrustProxies`** + **redirect 301 HTTPS** (plano 04, ADR-021).
- [ ] Service de **mídia** gera **WebP/AVIF** + variantes; componente Blade emite
      **`<picture>`/`srcset`** (nativo GD/Imagick — sem middleware reescrevendo HTML).
- [ ] `loading="lazy"`/`decoding="async"` abaixo da dobra; `fetchpriority="high"` no LCP.

## 5. Observabilidade e medição (plano 06/11)

- [ ] Middleware **`Server-Timing`** (`app;dur`).
- [ ] **RUM** mínimo e privacy-first (LCP/CLS/INP) via script vendorizado (zero dep) — ADR-019.
- [ ] **Lighthouse budget** (`lighthouse/budget.json`) no CI.
- [ ] Harness de medição (inspirado em `pesquisa-infra-cdn`: 24 indicadores + comparação com
      baseline) para não regredir.

## 6. SEO e higiene de cache (plano 14)

- [ ] `robots.txt` e `sitemap.xml` servidos **pela app** com status/`Content-Type` corretos.
- [ ] **404 real** (não redirect/soft-404).
- [ ] **Nunca** `Vary: User-Agent`; **`sw.js`** com `max-age=0, must-revalidate`.
- [ ] `sitemap.xml` sem escrita em disco, esquema por `url()`/`Request::secure()`.

## 7. Segurança de baseline (checklist do `minimal-web-security-gate`)

- [x] Gate `tools/security-check` (ou job CI): `APP_DEBUG=false`, `.env` fora do público,
      `display_errors=Off`, `expose_php=Off`, sessão `secure|httponly|samesite`, sem directory
      listing, headers presentes, sem debugbar em prod, **rate limit no login** (ADR-021).
- [ ] Alinhar ao **Decreto 9.637/2018** (segurança da informação) e à LGPD/LAI (ADR-019).

## 8. Testes e evidências

- [ ] `curl -I` conteúdo público: `Cache-Control: public…`, `ETag`, `Vary: Accept-Encoding`; `304`.
- [ ] Página com formulário: `private, no-cache`; sem token em HTML público.
- [ ] CSP sem `'nonce-'`/`'unsafe-inline'` e sem inline no HTML; `ETag` estável.
- [ ] Assets `immutable` + `content-encoding: br|gzip`.
- [ ] Imagem servida como `<picture>` com `image/webp`/`avif`; LCP com `fetchpriority`.
- [ ] `robots.txt`/`sitemap.xml` 200 com tipo correto; URL inexistente → `404`.
- [ ] `Server-Timing` presente; Lighthouse budget verde; RUM sem 3rd-party.
- [x] Gate `tools/security-check` verde.

## 9. Definition of Done

- [ ] Cache de resposta + ETag sem vazar CSRF e sem quebrar o CSP.
- [ ] Assets imutáveis + compressão + imagens modernas (sem middleware de reescrita).
- [ ] Observabilidade de performance (Server-Timing/RUM/budget) e gate de segurança de baseline.
- [ ] Nenhuma dependência nova; tudo nativo.

## 10. Riscos

| Risco | Mitigação |
|---|---|
| Cachear HTML com token/nonce | ADR-022: form pages `private`; CSP sem inline |
| FPC desatualizado após publicar | Invalidação por evento (se adotado) |
| Minify/CSP regressão | Não minificar HTML; CSP sem inline |
| WebP sem `Content-Type` | `AddType image/webp/avif` no servidor |
| `Vary: User-Agent` reintroduzido | Gate no `curl`/CI (lição v6) |
