# WHM/cPanel — Configuração manual da camada L0 (Performance)

- **Tarefa relacionada:** #23410 (Infra-Update)
- **Escopo:** servidor (WHM/root) — **não** conta cPanel, **não** `.htaccess`
- **Ref:** `plan/2026-08-09-infra-update.epic.md` (L0.1, L0.2) e `_pesquisa/security/`
- **Por que manual:** `opcache.*` e `realpath_cache_*` são `PHP_INI_SYSTEM` (o `.user.ini` da
  conta é **ignorado silenciosamente**) e a compressão via `.htaccess` é **sobreposta pelo vhost**
  do EasyApache4 (ineficaz — `compression=0,34` no baseline). Portanto estas duas frentes só
  existem no servidor. Não há script automatizado rodando no WHM: aplica-se o passo a passo abaixo.

---

## 0. Pré-requisitos e ordem

1. Acesso **WHM (root)** ou, para itens por-conta, **cPanel da conta**.
2. Aplicar nesta ordem: **P0‑A (compressão)** → **P0‑B (OPcache)** → **verificação**.
3. O `.htaccess` da conta (`public/.htaccess`) **já** contém os headers de estático
   (L0.3/L0.4) e o canário `X-Hardening-Version`. Não mexer em compressão/opcache ali.

---

## P0‑A · Compressão (gzip global + brotli)

### A.1 — Ativar gzip ("Optimize Website")

**Via cPanel da conta (mais simples, por domínio):**
1. Acesse o **cPanel** da conta do portal.
2. **Software » Optimize Website** (às vezes "Otimizar Site").
3. Selecione **"Compress all content"** (Comprimir todo o conteúdo).
4. Clique em **Save** (Salvar).
5. Isso habilita `mod_deflate` no vhost da conta para todos os tipos de texto.

**Via WHM (global, se quiser valer para todas as contas):**
1. WHM » **Service Configuration » Apache Configuration » Include Editor**
   (ou edite o include global de deflate).
2. Garanta que existe, no include de `pre_main_global` ou `pre_virtualhost_global`:
   ```apache
   <IfModule mod_deflate.c>
       AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
       AddOutputFilterByType DEFLATE application/javascript application/json application/xml
       AddOutputFilterByType DEFLATE application/x-javascript image/svg+xml font/ttf font/otf
   </IfModule>
   ```
3. **Save** e o Apache reinicia sozinho.

> Observação: o EasyApache4 já traz `mod_deflate`. A flag "Optimize Website" é o caminho validado
> (estágio‑1 provou **−62,1% no transfer**).

### A.2 — (Bônus, não bloqueia) Habilitar `mod_brotli`

Brotli é superior a gzip para texto. É módulo **opcional** do EasyApache4, habilitado no servidor:

1. WHM » **Software » EasyApache 4**.
2. Clique em **Customize** (em "Currently Installed Packages" ou "Customize Profiles").
3. Aba **Apache Modules**.
4. Na busca, digite **`brotli`** → marque **`mod_brotli`**.
5. Aba **Review** » **Provision** (rebuild). O Apache reinicia automaticamente.
6. (Opcional) Para usar brotli em vez de só gzip, adicione ao include do A.1:
   ```apache
   <IfModule mod_brotli.c>
       AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css text/javascript
       AddOutputFilterByType BROTLI_COMPRESS application/javascript application/json application/xml
       AddOutputFilterByType BROTLI_COMPRESS application/x-javascript image/svg+xml font/ttf font/otf
   </IfModule>
   ```
7. **Save/Provision** novamente.

---

## P0‑B · OPcache (ea‑php72)

> `opcache.*` é `PHP_INI_SYSTEM` → só no php.ini **de sistema** do EA4, não no `.user.ini` da conta.

> **STATUS (2026-09-09, phpinfo real): OPcache ATIVO** — `opcache.enable=On` e
> `opcache.validate_timestamps=Off` já confirmados em produção (phpinfo do canário). Aplicar
> ainda os valores de tuning abaixo (`memory_consumption` 128→256, `interned_strings_buffer`
> 8→32, `max_accelerated_files` 4000→10000) para subir o teto de arquivos cacheados.

### B.0 — CUIDADO: este é o php.ini DE SISTEMA (não cole o arquivo de conta aqui)

O `php.ini` aberto em "System" é global (`/opt/cpanel/ea-php72/root/etc/php.ini`) e o cPanel o
**regera em updates do EA4**. Por isso:

- **Só chaves `PHP_INI_SYSTEM` valem como alavanca de servidor aqui** (OPcache, realpath,
  `apcu.*`). Não cole o `php.ini.recomended` inteiro: ele contém endurecimento de CONTA
  (`expose_php`, `session.*`, limites, `pcre.jit`, `zend.assertions`) que são PERDIR/USER e
  devem ir no `php.ini`/`.user.ini` **de cada conta**.
- **NÃO use `error_log = "/home/USUARIO/..."` no global**: isso direciona o log de erro de
  *todas* as contas para um único diretório (ou falha). No sistema, deixe `error_log` no
  default (por conta) ou um path de servidor.
- Aplique via **WHM » MultiPHP INI Editor » System » ea-php72** (UI), não editando o arquivo
  na mão — assim a mudança sobrevive a rebuilds do EA4.
- Arquivo pronto para colar no editor System: **`whm.php.ini.recomended`** (raiz do repo) —
  contém só as chaves `PHP_INI_SYSTEM` (OPcache, realpath, APCu). Não confunda com
  `php.ini.recomended` (aquele é da CONTA).

### B.1 — Abrir o editor de sistema

1. WHM » **Software » MultiPHP INI Editor**.
2. No seletor superior, escolha **System** (não "Basic/Editor" de conta).
3. No seletor de versão, escolha **`ea-php72`** (a versão dos portais).
4. O editor abre o `php.ini` de sistema do EA4 (`/opt/cpanel/ea-php72/root/etc/php.ini`).

### B.2 — Definir os valores

Localize/adicione as chaves abaixo (valor desejado → produção):

```ini
opcache.enable = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 60
opcache.validate_timestamps = 0
opcache.save_comments = 1
realpath_cache_size = 4096K
realpath_cache_ttl = 600
```

- `opcache.validate_timestamps = 0` → em produção o OPcache **não** checa mtime a cada request
  (ganho de performance). **Exige reset de OPcache a cada deploy** (ver B.4).
- Se uma chave já existir, **substitua** o valor; não duplique a linha.

### B.3 — Salvar

1. Clique em **Save** (o WHM grava no php.ini de sistema e recarrega o PHP-FPM/Apache).
2. Se houver múltiplos handlers PHP-FPM por conta, o WHM reinicia o `php-fpm` do `ea-php72`.

### B.4 — Reset de OPcache no deploy (no app, controlável pela conta)

Como `validate_timestamps=0`, toda atualização de código precisa limpar o OPcache. No deploy
(`deploy.sh`) ou pós-release, uma destas ações:

```bash
# opção 1 — PHP
php -r 'opcache_reset();'

# opção 2 — tocar o index.php força recompile em alguns setups
touch /caminho/public/index.php

# opção 3 — reiniciar php-fpm (root)
whmapi1 restart_service service=php-fpm
# ou: service php-fpm restart
```

---

## P0‑C · APCu autoloader (ea‑php72)

> Complementar ao OPcache: o `apcu-autoloader` (já ativo no `composer.json` via
> `"apcu-autoloader": true`) guarda o **class map** do Composer na memória
> compartilhada do APCu, acelerando o autoload. A chave no `composer.json` só
> surte efeito se a **extensão APCu estiver instalada e habilitada** no servidor.

> **STATUS (2026-09-09): ATIVO (confirmado em produção).** O pacote RPM `ea-php72-php-apcu`
> **não existe** no repo EA4 deste servidor CloudLinux (`yum install ea-php72-php-apcu` falha
> com "Nenhuma correspondência" — no CloudLinux o APCu só vem via alt-php/PHP Selector, que
> **não se aplica** porque o domínio roda **ea-php72 (EA4)**, conforme phpinfo). Por isso a
> extensão foi instalada por **build manual**: `pecl install apcu` (gera `apcu.so` em
> `/opt/cpanel/ea-php72/root/usr/lib64/php/modules/` + ini em `php.d/zzz-apcu.ini`). O
> `apcu-autoloader` do `composer.json` (`"apcu-autoloader": true`) agora usa APCu de fato —
> rode `composer dump-autoload` para ativá-lo.
>
> ⚠️ **RISCO:** build manual é **sobrepujado por rebuild/update do EasyApache 4** — o
> `apcu.so` some e o PHP-FPM não sobe (`extension=apcu.so` órfão). Se isso ocorrer, remova a
> linha do ini ou reinstale via `pecl`. Documentado para não surpreender em futuros updates.

### C.1 — Instalar a extensão (EasyApache 4 — neste servidor: build manual)

> **Importante (CloudLinux + EA4):** neste servidor o pacote `ea-php72-php-apcu` **não
> existe** no repo EA4 (o `yum install ea-php72-php-apcu` falha com "Nenhuma correspondência").
> No CloudLinux o APCu só é entregue via **alt-php/PHP Selector**, que **não se aplica** porque
> o domínio roda **ea-php72 (EA4)** (phpinfo confirmado). Logo a única via funcional é **build
> manual via pecl** (método aplicado em produção em 2026-09-09).

**Método aplicado (root):**

1. Build manual com pecl (responda `no` ao prompt de debug interno):
   ```bash
   /opt/cpanel/ea-php72/root/usr/bin/pecl install apcu
   ```
   - Compila `apcu.so` e o instala em
     `/opt/cpanel/ea-php72/root/usr/lib64/php/modules/apcu.so`.
   - O pecl pode adicionar `extension=apcu.so` ao php.ini ou pedir confirmação; se não
     adicionar, crie o ini abaixo.
2. Criar o ini do APCu (scan dir do EA4):
   ```bash
   cat > /opt/cpanel/ea-php72/root/etc/php.d/zzz-apcu.ini <<'EOF'
   extension=apcu.so
   apcu.enabled=1
   apcu.enable_cli=1
   apc.shm_size=32M
   EOF
   ```
3. Reiniciar FPM:
   ```bash
   whmapi1 restart_service service=php-fpm
   # ou: /scripts/restartsrv_apache_php_fpm
   ```
4. Validar:
   ```bash
   /opt/cpanel/ea-php72/root/usr/bin/php -m | grep -i apcu   # apcu
   ```

> ⚠️ Build manual **não sobrevive a rebuild/update do EA4** (o `.so` é apagado do modules dir).
> Em todo update do EasyApache 4, revalide `php -m | grep apcu` e reinstale se sumir.

### C.2 — (Opcional) Beneficiar também o CLI (artisan/cron)

Por padrão o APCu em CLI vem desligado (`apcu.enable_cli = 0`), então o web
(php‑fpm) aproveita o cache mas `php artisan`/cron não. Para ligar no CLI,
no php.ini de sistema (WHM » **MultiPHP INI Editor » System » ea‑php72**):

```ini
apcu.enabled = 1
apcu.enable_cli = 1
apc.shm_size = 32M
```

> `apcu.enabled` e `apcu.enable_cli` são `PHP_INI_SYSTEM` → só no WHM, nunca no
> `.user.ini` da conta. `apc.shm_size` controla a memória compartilhada (32M
> costuma bastar para o class map; aumente se houver muitas classes).

### C.3 — Regenerar o autoloader no deploy

```bash
composer dump-autoload        # ou: composer install
# Composer deve indicar "with APCu" ao gerar os arquivos otimizados
```

### C.4 — Degradação graciosa

Se a extensão APCu **não** estiver instalada, o `composer dump-autoload` apenas
emite um *warning* ("The apcu-autoloader option is set but the extension is not
enabled") e segue **sem cache** — o deploy **não quebra**. Por isso `ext-apcu`
**não** foi adicionado ao `require` do `composer.json`.

---

## Verificação (passo a passo)

### Compressão (P0‑A)
```bash
# deve retornar Content-Encoding: gzip (ou br se brotli ativo)
curl -sI --compressed https://infra-update.devs.maxima.inf.br/ | grep -i content-encoding

# transfer deve cair; comparar vs baseline
python3 ../comparar/comparar.py measuring/results/2026-09-08-12-10 measuring/results/<nova-data>
# esperado: compression = 1.0 e transfer com queda (~ -62% no estagio-1)
```

### OPcache (P0‑B)
```bash
# no servidor, como root, no contexto do ea-php72:
/opt/cpanel/ea-php72/root/usr/bin/php -i | grep -E 'opcache.memory_consumption|opcache.validate_timestamps|opcache.enable'
# esperado: enable=1, memory_consumption=256, validate_timestamps=0

# pela rede (info da conta) não reflete opcache de sistema; confie no check acima.
```

### APCu autoloader (P0‑C)
```bash
# extensao presente no servidor (ea-php72)
/opt/cpanel/ea-php72/root/usr/bin/php -m | grep -i apcu
# ou: php -r "var_dump(extension_loaded('apcu'));"  -> bool(true)

# o autoloader gerado usa APCu
grep -l "apcu_fetch" vendor/composer/autoload_real.php && echo "APCu autoloader ATIVO"

# ao regerar, o Composer confirma
composer dump-autoload -v    # esperado: "... with APCu"
```

### Headers de estático (L0.3/L0.4 — já via .htaccess da conta)
```bash
curl -sI https://infra-update.devs.maxima.inf.br/assets/css/app.css \
  | grep -iE 'cache-control|etag|x-hardening-version'
# esperado:
#   Cache-Control: public, max-age=31536000, immutable
#   ETag: "..."
#   X-Hardening-Version: 2026-08-maxima   (prova que é o .htaccess correto)
```

### Guardrail (não regredir)
- `X-Hardening-Version` presente (segurança intacta).
- HTTP/2 + TLS 1.3 mantidos; redirects=0; disponibilidade=100%.
- `comparar.py` não piora `cache_policy`, `cache_hit`, `ttfb`, `transfer`.

---

## Rollback

- **Compressão:** cPanel » Optimize Website » "Disabled"; ou remover o include do A.1 no WHM.
- **Brotli:** WHM » EasyApache 4 » Customize » desmarcar `mod_brotli` » Provision.
- **OPcache:** WHM » MultiPHP INI Editor » System » `ea-php72` → restaure os valores originais
  (`memory_consumption=128`, `max_accelerated_files=16229`, `validate_timestamps=1`) e Salvar.
- **APCu:** ATIVO via build manual (`pecl install apcu`). Para reverter: remover
  `/opt/cpanel/ea-php72/root/etc/php.d/zzz-apcu.ini` (ou comentar `extension=apcu.so`) e
  reiniciar o FPM. O `"apcu-autoloader": true` no `composer.json` pode ficar (degrada
  graciosamente para warning se a extensão sumir em algum rebuild do EA4).

---

## Resumo de responsabilidade

| Item | Onde se faz | Quem | Risco |
|---|---|---|---|
| gzip (Optimize Website) | cPanel/WHM | root/conta | baixo |
| mod_brotli | WHM EasyApache 4 | root | baixo |
| OPcache ea-php72 | WHM MultiPHP INI Editor (System) | root | baixo |
| APCu autoloader (ea-php72) | **N/A neste ambiente** (ext não disponível; opcional, pulado) | — | — |
| reset OPcache no deploy | app / deploy.sh | deploy | baixo |
| Cache-Control/ETag estáticos | `public/.htaccess` (já feito) | repo | baixo |
