Files
claude-plugins/wordpress/skills/webp-express/SKILL.md
T
Claude Code 1ee91142d9 feat(wordpress): nova skill mcp-webp-express + doc MCP webpx na skill webp-express
Skill mcp-webp-express: MCP dedicado (webpx) para WebP Express multi-site
via WP-CLI/SSH, mesmo padrao mcp-wpfc. webp-express/SKILL.md: nova seccao
14 documentando o MCP construido e testado ao vivo em emanuelalmeida.pt.
2026-08-19 05:31:08 +01:00

702 lines
40 KiB
Markdown

---
name: webp-express
description: Gestão do plugin WebP Express (conversão automática de imagens para WebP) via WP-CLI e ficheiro de config em servidores CWP. Cobre localizar o ficheiro de config com hash variável, modos de operação (CDN friendly, Varied image responses, No conversion, Tweaked), reconversão em massa via `wp webp-express convert`, flush de webps, conversores disponíveis (imagemagick/imagick/gd vs cwebp/vips/ffmpeg/graphicsmagick/wpc/ewww) e as respectivas opções próprias, estrutura JSON completa do ficheiro de configuração (qualidade/encoding/near-lossless/alpha-quality por tipo de imagem, cache-control, alter HTML, redirection rules, web service), regras `.htaccess`, a fronteira com o Cloudflare, e o MCP `webpx` construído para gestão programática multi-site (§14). Usar quando "webp express", "converter imagens webp", "webp nao funciona", "reconverter imagens", "flush webp", "conversor imagemagick gd", "operation mode webp", "cdn friendly webp", "webp quality", "near lossless", "alter html webp", "cache control webp", "web service webp express", "mcp webp express", "webpx".
---
# /webp-express — Conversão de imagens para WebP via WP-CLI
Gestão programática do plugin WebP Express (`webp-express`) em sites WordPress
da frota, via WP-CLI no servidor CWP (`server.descomplicar.pt`, SSH porta
9443).
**Fonte:** `CONFIG-Plugins-Referencia.md` §5 (WebP Express) + verificação ao
vivo em `emanuelalmeida.pt` (versão `0.25.15`) + leitura completa do
código-fonte nesta sessão: `lib/classes/CLI.php`, `lib/classes/CachePurge.php`,
`lib/classes/BulkConvert.php`, e **todos** os ficheiros `.inc`/`.php` de
`lib/options/options/` (general, conversion-options incl.
`converter-options/*.php` por conversor, redirection-rules, serve-options,
alter-html, web-service-options, operation-mode.inc) — 43 ficheiros no total.
Config JSON completo lido ao vivo do disco (não só campos vistos na UI).
---
## Contexto CWP — sempre obrigatório
```bash
PATH=/home/USER/public_html
sudo -u USER /usr/local/bin/wp <comando> --path=$PATH
```
**Acesso via SSH:** `ssh -p 9443 root@server.descomplicar.pt` (alias `server`
já configurado localmente).
---
## 1. Onde vive a configuração — NÃO é `wp_options` normal
A config principal do WebP Express vive num **ficheiro JSON com nome
imprevisível** (hash no nome, muda por instalação/reinstalação):
```
wp-content/webp-express/config/config.<hash>.json
```
**Nunca assumir o hash.** Duas formas de o descobrir, por ordem de
preferência:
```bash
# 1. Forma rápida — o hash está guardado numa option (sem precisar de find)
wp option get webp-express-config-hash --path=$PATH
# 2. Confirmar/descobrir via find quando a option não existe ou for preciso
# validar o caminho real no disco
find $PATH/wp-content/webp-express/config -iname 'config.*.json'
```
O ficheiro em si:
```bash
HASH=$(wp option get webp-express-config-hash --path=$PATH)
cat $PATH/wp-content/webp-express/config/config.$HASH.json | python3 -m json.tool
```
### Estado operacional (`wp_options`, separado da config)
Ao contrário da config, o **estado** vive mesmo em `wp_options` normais,
todas com o prefixo `webp-express-`:
```bash
wp option get webp-express-state --format=json --path=$PATH
wp option get webp-express-migration-version --path=$PATH
wp option get webp-express-config-migrated-cve-2025-11379 --path=$PATH
wp option get webp-express-alter-html-options --format=json --path=$PATH
```
`webp-express-state` traz o resumo mais útil de uma só vez:
`workingConverterIds`, `workingAndActiveConverterIds`, `configured`,
`htaccess-rules-saved-at-some-point`, `active-htaccess-dirs`.
### ⚠️ Gotcha: `convert_webp_status` e `webp_offset_*` NÃO são do WebP Express
Numa auditoria com `wp option list --search='*webp*'` aparecem também
`convert_webp_status` e vários `webp_offset_*` (`webp_offset_thumbnail`,
`webp_offset_large`, etc.). **São opções órfãs de outro plugin** (não
presente na lista activa de plugins do site) — não pertencem ao WebP
Express e não devem ser lidas/alteradas ao auditar este plugin. Confirmar
sempre com `wp plugin list --format=json | grep -i webp` que só
`webp-express` está activo antes de interpretar options com "webp" no nome.
---
## 2. Modos de operação (`operation-mode`)
Fonte: `lib/options/options/operation-mode.inc`. Quatro valores possíveis:
| Valor | Nome na UI | Comportamento |
|---|---|---|
| `varied-image-responses` | Varied image responses | Redirecciona jpeg/png para webp via `.htaccess`, mas só quando o browser envia `Accept: image/webp`. A mesma URL responde de forma diferente consoante o cliente — **problemático para CDNs que não fazem `Vary` no header `Accept`** |
| `cdn-friendly` | CDN friendly | jpeg é **sempre** servido como jpeg; em vez de variar a resposta, o WebP Express reescreve o HTML (`<picture>`/`<img srcset>`) para apontar directamente ao ficheiro `.webp` estático. Melhor HIT ratio em CDN, sem exigir forward do header `Accept` |
| `no-conversion` | No conversion | Não converte nada — usado quando outro plugin já faz a conversão e só se quer aproveitar a reescrita de HTML/redirecção do WebP Express |
| `tweaked` | Tweaked | Sem preset — todas as opções ficam editáveis individualmente (mostra secções `redirection-rules` e `serve-options` completas, que ficam escondidas nos outros modos); ao voltar a um preset perdem-se as afinações |
**Verificado em `emanuelalmeida.pt`:** `operation-mode: "cdn-friendly"` —
escolha correcta dado o site estar atrás do Cloudflare (evita depender de o
Cloudflare fazer `Vary: Accept` correctamente, que nem sempre acontece nos
planos gratuitos).
```bash
HASH=$(wp option get webp-express-config-hash --path=$PATH)
wp eval "echo json_decode(file_get_contents('wp-content/webp-express/config/config.$HASH.json'))->{'operation-mode'};" --path=$PATH
```
Para mudar de modo é preciso editar o ficheiro JSON directamente (não há
comando `wp option patch` — não é uma option serializada) e depois correr
`wp webp-express convert --reconvert` para regenerar tudo com as novas
definições. **Mutação em produção — nunca fazer sem autorização.**
**Nota de UI condicional por modo** (relevante ao ler o código de
`lib/options/options/general/general.inc`): as secções `scope.inc`,
`destination-folder.inc` e `destination-structure.inc` só aparecem quando
`operation-mode != 'no-conversion'`; `cache-control.inc` só aparece em
`tweaked`, `varied-image-responses` ou `cdn-friendly`. O JSON de config
mantém sempre todos os campos gravados, independentemente do que a UI
mostra para o modo actual.
---
## 3. Reconverter imagens em massa — comando nativo `wp webp-express`
O plugin regista um comando WP-CLI próprio (`lib/classes/CLI.php`), **não é
preciso passar por `wp eval`**. Fonte lida na íntegra nesta sessão — o
plugin só regista **dois** subcomandos, não há mais:
```bash
wp help webp-express --path=$PATH
```
```
NAME
wp webp-express
SUBCOMMANDS
convert Convert images to webp
flushwebp Flush webps
```
### `wp webp-express convert`
```bash
# Ver o que falta converter, sem converter (dry preview por pasta)
wp webp-express convert --path=$PATH
# Converter tudo o que ainda não está convertido (media library + tema)
wp webp-express convert uploads --path=$PATH
wp webp-express convert themes --path=$PATH
# Forçar RECONVERSÃO de tudo (substitui webps já existentes)
wp webp-express convert uploads --reconvert --path=$PATH
# Limitar a uma subpasta
wp webp-express convert uploads/2026 --path=$PATH
# Só PNG / só JPEG
wp webp-express convert uploads --only-png --path=$PATH
wp webp-express convert uploads --only-jpeg --path=$PATH
# Forçar um conversor específico (ignora o stack de fallback)
wp webp-express convert uploads --converter=gd --path=$PATH
# Sobrepor qualidade/encoding só nesta corrida (não altera a config gravada)
wp webp-express convert uploads --quality=75 --encoding=lossy --path=$PATH
# Sobrepor near-lossless e alpha-quality só nesta corrida
wp webp-express convert uploads --near-lossless=60 --path=$PATH
wp webp-express convert uploads --alpha-quality=80 --path=$PATH
```
`<location>` aceita `uploads`, `themes`, `plugins`, `wp-content` ou `index`
como "raiz de imagem", opcionalmente seguido de `/subpasta`. Sem argumento,
lista quantos ficheiros por converter há em cada grupo, sem converter nada.
**Flags completas do `convert`** (confirmadas em `CLI.php`, docblock
`## OPTIONS`):
| Flag | Efeito | Aplica-se a `max-quality` E `png-quality` |
|---|---|---|
| `[<location>]` | Restringe a uma raiz de imagem (`uploads`\|`themes`\|`plugins`\|`wp-content`\|`index`), opc. `/subpasta` | — |
| `--reconvert` | Reconverte mesmo o que já está convertido | — |
| `--only-png` | Só PNG (`image-types = 2`) | — |
| `--only-jpeg` | Só JPEG (`image-types = 1`) | — |
| `--quality=<n>` | Sobrepõe qualidade (0-100) | Sim — aplica ao mesmo valor `max-quality` e `png-quality` |
| `--near-lossless=<n>` | Sobrepõe near-lossless (0-100) | Sim — aplica a `png-near-lossless` e `jpeg-near-lossless` |
| `--alpha-quality=<n>` | Sobrepõe alpha-quality (0-100) | Só `alpha-quality` (PNG com transparência) |
| `--encoding=<auto\|lossy\|lossless>` | Sobrepõe encoding | Sim — aplica a `png-encoding` e `jpeg-encoding` |
| `--converter=<id>` | Força um conversor específico, ignora o stack. Valores válidos: `cwebp \| vips \| ewww \| imagemagick \| imagick \| gmagick \| graphicsmagick \| ffmpeg \| gd \| wpc` | — |
Nenhuma destas flags altera a config gravada em disco — são só para essa
corrida.
### `wp webp-express flushwebp` — limpar webps gerados
```bash
# Apaga TODOS os .webp gerados (força reconversão no próximo acesso/convert)
wp webp-express flushwebp --path=$PATH
# Só os que vieram de PNG
wp webp-express flushwebp --only-png --path=$PATH
```
Internamente chama `CachePurge::purge()` (`lib/classes/CachePurge.php`),
que apaga recursivamente `.webp` em três locais: o cache dir
(`Paths::getCacheDirAbs()`), o upload dir (só se
`destination-folder == 'mingled'`) e o dir de "bigger than source dummy
files". Nunca apaga o original de um attachment registado na media library
(`MediaLibraryHelper::isRegisteredAttachmentOrThumbnail`) — protecção
interna contra apagar acidentalmente ficheiros errados quando modo
`mingled` mistura originais e webps na mesma pasta.
Útil depois de mudar `quality`/`encoding`/conversor na config, antes de
correr `convert` de novo — garante que não ficam webps antigos com as
definições anteriores misturados com os novos.
**Alternativa via wp-admin (sem SSH):** Settings → WebP Express → aba
"Bulk convert" tem os mesmos botões ("Bulk Convert" → popup de conversão em
massa via AJAX; "Delete converted files" → popup que chama
`processAjaxPurgeCache()`, o mesmo `CachePurge::purge()` do `flushwebp`).
Há ainda um botão "Delete log files" na secção "Enable logging" (ver §7).
O WP-CLI é preferível em produção por ser scriptável e não depender de
timeout do browser em sites com muitas imagens. **Nota:** a conversão em
massa via UI usa sempre as **últimas definições gravadas** (comentário no
código-fonte de `bulk-convert.inc`: *"the bulk conversion is using the
last saved settings"*) — se acabaste de mudar qualidade/encoding no forms
mas ainda não gravaste (Save), o bulk convert usa a versão antiga.
---
## 4. Conversores — stack de fallback e opções por conversor
`webp-express` tenta uma lista ordenada de conversores até um funcionar; cada
entrada tem `working: true/false` gravado em `webp-express-state` (testado
no arranque, não em cada conversão):
| Conversor | Estado em `emanuelalmeida.pt` | Nota |
|---|---|---|
| `imagemagick` (binário CLI) | ✅ `working: true`¹ | Fallback preferido |
| `imagick` (extensão PHP) | ✅ `working: true`¹ | |
| `gd` (extensão PHP, sempre disponível) | ✅ `working: true` | Último recurso, sempre presente em qualquer instalação PHP |
| `cwebp` (binário Google) | ❌ `working: false` | Não instalado no servidor CWP |
| `vips` | ❌ `working: false` | Não instalado |
| `graphicsmagick`/`gmagick` | ❌ `working: false` | Não instalado |
| `ffmpeg` | ❌ `working: false` | Não instalado |
| `wpc` (WebP Cloud / Remote WebP Express, API paga ou remota) | ❌ `working: false` | Não configurado (sem API key/URL) |
| `ewww` (requer plugin EWWW Image Optimizer) | ❌ `working: false` | Plugin não instalado neste site |
¹ Nota lida directamente no JSON de config nesta sessão: no bloco
`converters[]` do config actual, `imagemagick` e `graphicsmagick` aparecem
com `"working": false` (o binário de sistema não responde), enquanto só
`gd` tem `"working": true`. O `webp-express-state` (cache separada,
recalculada a cada teste de auto-detecção) pode divergir ligeiramente do
snapshot gravado no `converters[]` do JSON — **confirmar sempre pelo
`webp-express-state` para saber o estado corrente**, o array `converters`
do config json é sobretudo a lista de opções por conversor + ordem.
**Sem impacto operacional** — `gd` cobre 100% dos casos como último
recurso; os conversores em falta são apenas alternativas mais
rápidas/melhor compressão que não compensam instalar a nível de servidor
só para este plugin. Verificar sempre `workingConverterIds` antes de forçar
`--converter=X` num `wp webp-express convert` — se `X` não estiver na
lista, o comando falha.
```bash
wp option get webp-express-state --format=json --path=$PATH | python3 -c "import json,sys; print(json.load(sys.stdin)['workingConverterIds'])"
```
### Opções específicas por conversor (bloco `converters[].options` no JSON)
Fonte: `lib/options/options/conversion-options/converter-options/*.php` (um
ficheiro por conversor) + o array `converters` do config JSON ao vivo.
| Conversor | Opções (`options{}`) | Descrição |
|---|---|---|
| `cwebp` | `use-nice` (bool), `try-common-system-paths` (bool), `try-supplied-binary-for-os` (bool), `method` (0-6), `low-memory`/"set-size"+`size-in-percentage`, `command-line-options` (string livre passada ao binário) | `method`: trade-off velocidade/qualidade (0=rápido, 6=melhor). "Size" força tamanho-alvo em % do original em vez de qualidade (~2.5x mais lento). `command-line-options` aceita qualquer flag do `cwebp` real, ex. `-low_memory -af -f 50 -sharpness 0 -mt` |
| `vips` | `smart-subsample` (bool), `preset` (`none`\|`default`\|`photo`\|`picture`\|`drawing`\|`icon`\|`text`) | Preset ajusta várias opções internas consoante o tipo de conteúdo (não sobrepõe qualidade) |
| `imagemagick` | `use-nice` (bool) | Executa o binário `convert`; sem outras opções específicas |
| `graphicsmagick` (`gmagick`) | `use-nice` (bool) | Executa `gm convert`; sem outras opções específicas |
| `ffmpeg` | `use-nice` (bool), `method` (0-6) | Idêntico ao `imagemagick` mas via ffmpeg |
| `gd` | `skip-pngs` (bool) | Se `true`, PNGs passam para o próximo conversor da stack (Gd historicamente pior a comprimir PNG e teve bugs de transparência, hoje resolvidos) |
| `imagick` | — (sem opções próprias) | "imagick has no special options" no código |
| `ewww` | `api-key`, `api-key-2` (fallback) | Serviço cloud pago (ewww.io); fee único, sem custo por conversão webp |
| `wpc` (Remote WebP Express / WPC) | `api-version` (0\|1), `api-url`, `secret`/`api-key`, `crypt-api-key-in-transfer` (bool) | Delega a conversão a outra instalação WebP Express ou a um serviço `webp-convert-cloud-service`. Api 0 é legado (v0.1); usar 1 para instâncias WebP Express normais |
`use-nice`: presente em quase todos os conversores baseados em binário —
executa com prioridade reduzida (`nice`) para poupar recursos à custa de
conversão ligeiramente mais lenta.
`Use nice`, `try-common-system-paths`, `try-supplied-binary-for-os`: o
plugin traz binários `cwebp` pré-compilados para várias plataformas dentro
do próprio plugin (`try-supplied-binary-for-os`) — útil quando o binário de
sistema não existe mas o hosting permite `exec()`.
---
## 5. Qualidade, encoding, near-lossless e metadata (`conversion-options`)
Fonte: `lib/options/options/conversion-options/{quality,jpeg,png,metadata}.inc`
+ valores ao vivo do JSON de `emanuelalmeida.pt`.
### 5.1. JPEG → WebP
| Campo JSON | Valor ao vivo | Descrição |
|---|---|---|
| `jpeg-encoding` | `"auto"` | `lossy` (sempre lossy) ou `auto` (o plugin experimenta lossy e lossless e fica com o mais pequeno). `Gd`/`Ewww` não suportam `auto` |
| `quality-auto` | `true` | Se `true`, tenta detectar a qualidade original do jpeg e usar a mesma no webp (requer imagick/imagemagick/gmagick capazes de detectar qualidade); se `false`, usa `quality-specific` fixo |
| `max-quality` | `80` | Tecto para `quality-auto`: mesmo que o jpeg original tenha qualidade mais alta, nunca ultrapassa este valor. Recomendado 50-85 |
| `quality-specific` | `70` | Qualidade fixa usada quando `quality-auto=false` OU como fallback quando a detecção de qualidade falhar |
| `jpeg-enable-near-lossless` | `true` | Aplica-se quando o encoding resultante é lossless: liga o pré-processamento "near-lossless" em vez de 100% lossless puro. Só suportado por `cwebp` e `vips` |
| `jpeg-near-lossless` | `60` | Nível 0 (máximo pré-processamento) a 100 (sem pré-processamento, = lossless puro) |
### 5.2. PNG → WebP
| Campo JSON | Valor ao vivo | Descrição |
|---|---|---|
| `png-encoding` | `"auto"` | `lossless` (default para PNG) ou `auto` (experimenta lossy e lossless, fica com o mais pequeno — não existe opção "sempre lossy" isolada para PNG na UI) |
| `png-quality` | `85` | Qualidade quando o resultado é lossy (via `auto`). Recomendado 60-90, tipicamente mais alto que jpeg pois PNG é usado para ícones/gráficos |
| `alpha-quality` | `80` | Qualidade específica do canal alfa (transparência), só relevante em encoding lossy com transparência. `Gd`/`Ewww` ignoram esta opção e mantêm o canal alfa sempre lossless |
| `png-enable-near-lossless` | `true` | Idêntico ao `jpeg-enable-near-lossless`, mas para PNG |
| `png-near-lossless` | `60` | Idêntico ao `jpeg-near-lossless` |
### 5.3. Metadata
| Campo JSON | Valor ao vivo | Descrição |
|---|---|---|
| `metadata` | `"none"` | `none` (não copia EXIF/etc. para o webp — mais pequeno) ou `all` (copia toda a metadata). **`Gd` não suporta cópia de metadata** — ignora esta opção sempre que `Gd` é o conversor efectivo |
### 5.4. Outras opções gerais de conversão
| Campo JSON | Valor ao vivo | Descrição |
|---|---|---|
| `convert-on-upload` | `true` | Converte a imagem (incl. todos os thumbnails gerados) no momento do upload para a media library. Hooka em `handle_upload` e `image_make_intermediate_size`. Pode abrandar o upload em temas com muitos tamanhos de thumbnail |
| `image-types` | `3` | Bitmask: `1`=jpeg, `2`=png, `3`=ambos, `0`=desactivado. Controla que tipos são convertidos/processados |
| `prevent-using-webps-larger-than-original` | `true` | Webps maiores que o original ficam sempre em disco mas não são usados (nem em `.htaccess` nem em Alter HTML) se esta opção estiver activa. Desde 0.25.5: em Alter HTML com `picture` + `srcset`, só omite o `<source webp>` se **todos** os webps do srcset forem maiores — considerar desactivar se muitas imagens têm srcset |
| `enable-logging` | `false` | Grava resultado de cada conversão em `wp-content/webp-express/log/conversions/`. Tem botão dedicado "Delete log files" na UI (popup `purgelogpopup`) |
---
## 6. Estrutura completa do ficheiro de configuração (JSON)
Volcado ao vivo de `emanuelalmeida.pt`
(`wp-content/webp-express/config/config.<hash>.json`) — esta é a **lista
completa de todas as chaves de topo** que o plugin grava, incluindo as que
não aparecem directamente numa única aba da UI:
```jsonc
{
// --- Modo e âmbito ---
"operation-mode": "cdn-friendly", // varied-image-responses | cdn-friendly | no-conversion | tweaked
"scope": ["uploads", "themes"], // subset de: uploads, themes, plugins, wp-content, index
"image-types": 3, // bitmask: 1=jpeg 2=png 3=ambos 0=nenhum
// --- Destino dos ficheiros webp ---
"destination-folder": "separate", // separate | mingled
"destination-extension": "append", // append (image.jpg.webp) | set (image.webp)
"destination-structure": "image-roots", // doc-root | image-roots
// --- Cache-Control header ---
"cache-control": "no-header", // no-header | set | custom
"cache-control-custom": "public, max-age=31536000, stale-while-revalidate=604800, stale-if-error=604800",
"cache-control-max-age": "one-week", // one-second|one-minute|one-hour|one-day|one-week|one-month|one-year
"cache-control-public": false, // true=public, false=private (só usado quando cache-control=set)
// --- Robustez ---
"prevent-using-webps-larger-than-original": true,
"enable-logging": false,
// --- Redirection rules (.htaccess) ---
"enable-redirection-to-converter": false, // só editável em modo "tweaked"/"varied-image-responses"
"only-redirect-to-converter-on-cache-miss": false,
"only-redirect-to-converter-for-webp-enabled-browsers": true,
"do-not-pass-source-in-query-string": true,
"redirect-to-existing-in-htaccess": false,
"forward-query-string": true,
"enable-redirection-to-webp-realizer": true, // "criar webp sob pedido" (chave em cdn-friendly)
// --- Qualidade/encoding jpeg ---
"jpeg-encoding": "auto", // lossy | auto
"jpeg-enable-near-lossless": true,
"jpeg-near-lossless": 60,
"quality-auto": true,
"max-quality": 80,
"quality-specific": 70,
// --- Qualidade/encoding png ---
"png-encoding": "auto", // lossless | auto
"png-enable-near-lossless": true,
"png-near-lossless": 60,
"png-quality": 85,
"alpha-quality": 80,
// --- Stack de conversores, ordem = prioridade ---
"converters": [
{ "converter": "cwebp", "options": { "use-nice": true, "try-common-system-paths": true, "try-supplied-binary-for-os": true, "method": 6, "low-memory": true, "command-line-options": "" }, "working": false },
{ "converter": "vips", "options": { "smart-subsample": false, "preset": "none" }, "working": false },
{ "converter": "imagemagick", "options": { "use-nice": true }, "working": false },
{ "converter": "graphicsmagick", "options": { "use-nice": true }, "working": false },
{ "converter": "ffmpeg", "options": { "use-nice": true, "method": 4 }, "working": false },
{ "converter": "wpc", "working": false, "options": { "api-key": "" } },
{ "converter": "ewww", "working": false },
{ "converter": "imagick", "working": false },
{ "converter": "gmagick", "working": false },
{ "converter": "gd", "options": { "skip-pngs": false }, "working": true }
],
// --- Metadata / upload automático ---
"metadata": "none", // none | all
"convert-on-upload": true,
// --- Serve options (resposta ao browser) ---
"fail": "original", // original | 404 | report
"success-response": "original", // original | converted
// --- Alter HTML ---
"alter-html": {
"enabled": true,
"replacement": "picture", // picture | url
"hooks": "ob", // content-hooks | ob (output buffering completo da página)
"only-for-webp-enabled-browsers": false,
"only-for-webps-that-exists": false,
"alter-html-add-picturefill-js": true,
"hostname-aliases": [] // hostnames extra (CDN) cujas URLs também devem ser reescritas
},
// --- Web service (partilhar conversão com outros sites) ---
"web-service": {
"enabled": false,
"whitelist": [] // [{ip, label, api-key, crypt-in-transfer}, ...]
},
// --- Snapshot de ambiente à data do save (diagnóstico, não editável) ---
"environment-when-config-was-saved": { "...": "..." },
"base-htaccess-on-these-capability-tests": { "...": "..." },
"document-root": "/home/USER/site",
"paths-used-in-htaccess": { "wod-url-path": "wp-content/plugins/webp-express/wod/webp-on-demand.php" }
}
```
### 6.1. Cache-Control header — detalhe
Fonte: `lib/options/options/general/cache-control.inc`.
| `cache-control` | Comportamento |
|---|---|
| `no-header` | Não define header — deixa o servidor/CDN decidir |
| `set` | Usa `cache-control-public` (public/private) + `cache-control-max-age` (preset de duração) para montar o header automaticamente |
| `custom` | Usa literalmente a string em `cache-control-custom` — **única forma de definir `stale-while-revalidate`/`stale-if-error`**, não expostos nos presets |
O texto de ajuda do próprio plugin recomenda `custom` com
`stale-while-revalidate`/`stale-if-error` para melhor resiliência em falhas
momentâneas do servidor de origem.
### 6.2. Scope — mapeamento de valores
Fonte: `lib/options/options/general/scope.inc`. O array `scope[]` é
qualquer subconjunto ordenado alfabeticamente de `uploads`, `themes`,
`plugins`, `wp-content`, `index`; a UI mapeia combinações comuns para
nomes amigáveis:
| `scope` (array) | Nome na UI |
|---|---|
| `["uploads"]` | Uploads only |
| `["themes"]` | Themes only |
| `["themes", "uploads"]` | Uploads and themes |
| `["plugins", "themes", "uploads", "wp-content"]` | All content |
| `["index", "plugins", "themes", "uploads", "wp-content"]` | Everything (including wp-admin) |
| Qualquer outra combinação | Mostrado como "Custom: …" |
**Verificado em `emanuelalmeida.pt`:** `["uploads", "themes"]` → "Uploads
and themes" (plugins/wp-content fora de âmbito).
### 6.3. Destination folder / extension / structure
| Opção | Valores | Efeito |
|---|---|---|
| `destination-folder` | `separate` (pasta dedicada `wp-content/webp-express/webp-images/`) \| `mingled` (webp ao lado do original, só dentro de `uploads`) | `mingled` é recomendado ao combinar com plugins de cache de página que sabem servir por extensão (ex. Cache Enabler), ou com Shortpixel |
| `destination-extension` | `append` (`imagem.jpg.webp`) \| `set` (`imagem.webp`) | `set` conflitua se existir `logo.jpg` E `logo.png` na mesma pasta (colidem no mesmo `logo.webp`) |
| `destination-structure` | `doc-root` (espelha caminho a partir da doc root) \| `image-roots` (espelha caminho a partir da raiz de imagem — uploads/themes/etc) | Recomendado `image-roots` em hosts com doc root mal configurada; `doc-root` reduz regras de rewrite necessárias em Nginx |
**Verificado em `emanuelalmeida.pt`:** `destination-folder: "separate"`,
`destination-extension: "append"`, `destination-structure: "image-roots"`.
---
## 7. Redirection rules (`.htaccess`) — detalhe por opção
Fonte: `lib/options/options/redirection-rules/*.inc`. Estas opções só são
todas editáveis em modo `tweaked`; nos outros modos, o preset do modo
decide-as automaticamente (mas o valor gravado no JSON reflecte sempre o
que está realmente activo em `.htaccess`).
| Campo JSON | Descrição |
|---|---|
| `redirect-to-existing-in-htaccess` | Redirect interno directo para o `.webp` já existente, sem passar por PHP — mais rápido. Regra fica **acima** da regra de conversão |
| `enable-redirection-to-converter` | Redirige jpeg/png (não convertidos) para `webp-on-demand.php`, que converte, grava e serve. Regra fica **abaixo** da anterior (só actua se ainda não existir webp) |
| `enable-redirection-to-webp-realizer` | "Criar webp files upon request": redirige pedidos por um `.webp` que **ainda não existe** para `webp-realizer.php`, que procura o jpg/png correspondente, converte e serve — permite referenciar webps no HTML antes de existirem fisicamente. É a opção chave em modo `cdn-friendly` |
| `only-redirect-to-converter-for-webp-enabled-browsers` | Só actua a regra do `enable-redirection-to-converter` quando o `Accept` header contém `image/webp` |
| `only-redirect-to-converter-on-cache-miss` | Condição extra equivalente à funcionalidade usada por `enable-redirection-to-webp-realizer`, mas aplicada ao `enable-redirection-to-converter` — desnecessária se `redirect-to-existing-in-htaccess` já estiver activo |
| `do-not-pass-source-in-query-string` | Se `true` (default recomendado), passa o path da imagem por env var em vez de query string, para não disparar firewalls que bloqueiam certas query strings |
| `forward-query-string` | Encaminha a query string original do pedido para o script de conversão |
---
## 8. Serve options (resposta ao browser após conversão)
Fonte: `lib/options/options/serve-options/*.inc` — só visível na UI em modo
`tweaked`.
| Campo JSON | Valores | Descrição |
|---|---|---|
| `fail` | `original` (serve o jpg/png original) \| `404` \| `report` (relatório de erro em texto simples) | Recomendado `original` em produção |
| `success-response` | `original` \| `converted` | `converted` envia header `Vary: Accept` (indica que a resposta depende do Accept). Usar `original` se combinado com plugins de cache de página que não sabem variar por Accept (ex. Cache Enabler sem suporte dual-cache) |
**Verificado em `emanuelalmeida.pt`:** `fail: "original"`,
`success-response: "original"`.
---
## 9. Alter HTML — detalhe completo
Fonte: `lib/options/options/alter-html/*.inc`.
| Campo JSON (`alter-html.*`) | Valores | Descrição |
|---|---|---|
| `enabled` | bool | Recomendado activar mesmo com redirecção activa: melhora cache em CDN e mantém extensão correcta ao fazer download de imagem |
| `replacement` | `picture` \| `url` | **`picture`**: substitui `<img>` por `<picture>` com `<source type="image/webp">` — funciona bem com cache de página, mas pode quebrar CSS `div > img` (usar `div img`). **`url`**: substitui directamente os URLs de imagem (src, srcset, lazy-load attrs, inline styles) — mais abrangente mas não funciona com cache de página excepto Cache Enabler |
| `hooks` | `content-hooks` \| `ob` | `content-hooks`: hooka em `the_content`, `the_excerpt`, `post_thumbnail_html`, `get_avatar`, `woocommerce_product_get_image`, `acf_the_content`, `dynamic_sidebar_before/after` — mais leve. `ob`: intercepta a página inteira via output buffering — necessário se o tema/plugin não usa os hooks standard. HTML > 600kb ignora a alteração por protecção de memória |
| `only-for-webp-enabled-browsers` | bool | Só relevante com `replacement=url`: só faz a substituição quando o browser suporta webp (evita servir só-webp a browsers antigos sem JS de fallback) |
| `only-for-webps-that-exists` | bool | Se `false` ("Reference webps that haven't been converted yet" activo), permite referenciar webps que ainda não existem — requer `enable-redirection-to-webp-realizer` activo para funcionar |
| `alter-html-add-picturefill-js` | bool | Só relevante com `replacement=picture`: injecta polyfill `picturefill.js` para browsers sem suporte nativo a `<picture>` (~94% suporte nativo actual) |
| `hostname-aliases` | array de strings | Hostnames adicionais (ex. CDN) cujas imagens também devem ser processadas — útil se outro plugin já reescreve URLs para um CDN antes do WebP Express actuar |
**Verificado em `emanuelalmeida.pt`:** `enabled: true`,
`replacement: "picture"`, `hooks: "ob"`,
`only-for-webp-enabled-browsers: false`,
`only-for-webps-that-exists: false`,
`alter-html-add-picturefill-js: true`, `hostname-aliases: []`.
---
## 10. Web service — partilhar conversão entre sites
Fonte: `lib/options/options/web-service-options/*.inc`. Permite que **este**
site actue como servidor de conversão remota para outros sites que usam o
conversor `wpc`/"Remote WebP Express" (ver §4).
| Campo JSON (`web-service.*`) | Descrição |
|---|---|
| `enabled` | Activa o endpoint (URL exposto: `Paths::getWebServiceUrl()`) |
| `whitelist[]` | Lista de sites autorizados a usar este endpoint, cada entrada com: `label` (referência própria), `ip` (aceita wildcard `*`, ex. `212.91.*` ou `*` para qualquer IP), `api-key` (chave arbitrária escolhida pelo utilizador, não um segredo gerado), `crypt-api-key-in-transfer` (bool — cifra a api-key no transporte; alguns setups antigos não suportam) |
**Verificado em `emanuelalmeida.pt`:** `web-service.enabled: false`,
`whitelist: []` — não usado como servidor remoto neste site.
---
## 11. Bulk convert e cache purge (via UI e via CLI)
Fonte: `lib/options/options/conversion-options/bulk-convert.inc` +
`lib/classes/BulkConvert.php` + `lib/classes/CachePurge.php`.
- **UI → "Bulk Convert"** dispara AJAX que usa `BulkConvert::getList()` +
conversão sequencial das últimas definições **gravadas**. Equivalente
CLI: `wp webp-express convert <scope> [--reconvert]`.
- **UI → "Delete converted files"** dispara AJAX
`CachePurge::processAjaxPurgeCache()`. Equivalente CLI:
`wp webp-express flushwebp [--only-png]`.
- **UI → "Delete log files"** (dentro da secção "Enable logging") apaga
`wp-content/webp-express/log/conversions/*`; sem equivalente CLI
dedicado — usar `wp eval` ou apagar directamente via `find`/`rm` se
necessário (nunca sem autorização em produção).
- O filtro de listagem interno (`BulkConvert::defaultListOptions`) usa
sempre `only-unconverted: true` por default (não reconverte o que já
existe) e `max-depth: 100` — nunca varre mais de 100 níveis de
subpastas.
- `CachePurge::purge()` nunca apaga `.webp` que estejam registados como
attachment/thumbnail na media library (protecção em modo `mingled`, onde
originais e webps partilham pasta).
---
## 12. Regras `.htaccess`
`webp-express-state.configured: true` e
`htaccess-rules-saved-at-some-point: true` confirmam que as regras já foram
gravadas — normalmente na pasta de cache do próprio plugin
(`active-htaccess-dirs: ["cache"]`), não na raiz do site. Isto é o que faz
o browser/Cloudflare receberem directamente o ficheiro `.webp` estático em
vez de passar por PHP a cada pedido (modo `cdn-friendly`).
Se `configured: false` ou o site deixar de servir webps depois de uma
migração/restauro, o passo de correcção é voltar à página de definições do
plugin (wp-admin) e gravar de novo (o botão "Save" regrava o `.htaccess`) —
não há comando WP-CLI dedicado a isto; o mais próximo é
`wp webp-express flushwebp` seguido de `convert`, que não recria as regras
`.htaccess` por si só.
---
## 13. Interacção com Cloudflare — quem faz o quê
**Achado desta sessão, importante para não duplicar trabalho:** a
conversão/optimização de imagem feita pelo **Cloudflare "Polish"** está
`off` neste site (não editável no plano Free) — o WebP não vem do
Cloudflare, vem inteiramente do WebP Express a correr no WordPress. O
Cloudflare aqui é só **cache/CDN de distribuição** do ficheiro `.webp` já
gerado, não faz qualquer transformação de imagem própria. Não há
sobreposição de funcionalidade a resolver, mas também não há optimização
de imagem "grátis" via Cloudflare a contar — se o WebP Express falhar a
converter, o Cloudflare não compensa.
Modo `cdn-friendly` foi escolhido precisamente por causa disto: ao reescrever
o HTML para apontar directamente ao `.webp` estático (em vez de depender do
`Accept` header e do Cloudflare fazer `Vary` correctamente), garante HIT
ratio alto no cache do Cloudflare independentemente do plano.
---
## 14. MCP `webpx` — gestão programática (construído 19-08-2026)
Existe um MCP dedicado (`mcp-webp-express`, registado como `webpx` em
`~/.omp/agent/mcp.json`) que expõe as operações deste documento como tools,
multi-site (todos os 8 aliases do bundle Descomplicar, mesmo padrão do
`wpfc`/`mcp-element-pack`): `spawn("ssh", ...)`, valor em base64 sobre
stdin, sem ledger/rollback (fora do âmbito — ver `emcp-tools` para mudanças
que precisem disso).
**Diferença chave vs os outros MCPs da frota:** a config deste plugin vive
num ficheiro com hash no nome (§1), não numa `wp_option` — o MCP resolve o
hash via `webp-express-config-hash` em cada chamada (nunca hardcoded) e
preserva owner:group:permissões do ficheiro ao escrever (a ligação SSH
corre como root; sem isto o wp-admin do site deixaria de conseguir gravar
as próprias definições). `webp-express-state` também exigiu tratamento
especial: o plugin grava-a como STRING JSON, não como array — um
`--format=json` ingénuo devolve JSON duas vezes codificado.
### Tools (9)
- `webpx_list_sites` — sites conhecidos.
- `webpx_get_status` / `webpx_get_state` — plugin instalado/activo/versão +
`webp-express-state` (workingConverterIds, configured, htaccess-rules-
saved-at-some-point).
- `webpx_get_config` / `webpx_set_config` — ficheiro `config.<hash>.json`
completo (§6).
- `webpx_get_converters` / `webpx_set_converter_options` — stack de
conversores (§4), opções por conversor + reordenação.
- `webpx_convert` — `wp webp-express convert` (§3).
- `webpx_flushwebp` — `wp webp-express flushwebp` (§3).
### Guarda-corpos
- `webpx_set_config` recusa gravar as chaves de diagnóstico só-leitura de
§6 (`environment-when-config-was-saved`,
`base-htaccess-on-these-capability-tests`, `document-root`,
`paths-used-in-htaccess`).
- `webpx_set_config` devolve um aviso explícito quando a chave alterada só
tem efeito via `.htaccess` (operation-mode, scope, image-types,
destination-*, redirection rules) — regenerar essas regras continua a
exigir abrir wp-admin → Save uma vez (§12), sem equivalente WP-CLI.
**Testado ao vivo em `emanuelalmeida.pt` (19-08-2026):** leitura completa
da config, escrita+leitura de volta com `enable-logging` (revertido),
escrita+leitura de volta de `gd.skip-pngs` via `webpx_set_converter_options`
(revertido), bloqueio confirmado ao tentar gravar `document-root`, `convert`
em modo preview (0 ficheiros por converter, config já estável). Owner:group
do ficheiro de config confirmado `ealmeida:ealmeida 644` antes e depois da
escrita. `webpx_flushwebp` e `convert` com `location`/`reconvert` reais não
foram exercidos contra produção (mesmo padrão de comando já validado via
`convert` em dry-run — risco desnecessário para a validação).
## Erros comuns
| Sintoma | Causa | Solução |
|---|---|---|
| `find`/`cat` no ficheiro de config falha, "No such file" | Hash do nome do ficheiro mudou (reinstalação/migração) | Nunca hardcode o hash — ler sempre `wp option get webp-express-config-hash` primeiro |
| Confundir `convert_webp_status`/`webp_offset_*` com definições do WebP Express | Options órfãs de outro plugin já removido, mesmo prefixo textual "webp" | Confirmar `wp plugin list` antes de interpretar qualquer option com "webp" no nome |
| `wp webp-express convert --converter=cwebp` falha | Conversor não está na lista `workingConverterIds` (binário não instalado no servidor) | Verificar `webp-express-state` antes de forçar um conversor específico |
| Imagens não aparecem como WebP no browser depois de `convert` | `.htaccess` não gravado (`configured: false`) ou modo `varied-image-responses` com CDN sem `Vary: Accept` | Confirmar `configured: true` em `webp-express-state`; preferir `cdn-friendly` atrás de Cloudflare |
| Webps antigos com qualidade errada persistem depois de mudar `quality`/`encoding` na config | `convert` normal só converte o que falta, não regenera o que já existe | `wp webp-express flushwebp` seguido de `wp webp-express convert` (ou `convert --reconvert` directo) |
| Bulk convert via UI usa qualidade/encoding antigos apesar de ter mudado o form | O bulk convert usa sempre as **últimas definições gravadas** (Save), não o estado não-gravado do formulário | Gravar (Save) antes de disparar "Bulk Convert" |
| Metadata (EXIF) desaparece do webp apesar de `metadata: "all"` | `Gd` está a ser o conversor efectivo — Gd não suporta cópia de metadata, ignora sempre a opção | Confirmar qual conversor está `working`/activo antes de assumir que `metadata: "all"` está a ser respeitado |
| Alpha quality/near-lossless parecem não ter efeito | Conversor efectivo (`Gd`/`Ewww`) não suporta essas opções — são ignoradas silenciosamente | Confirmar o conversor: near-lossless só em `cwebp`/`vips`; alpha-quality ignorado por `Gd`/`Ewww` |
| Selector CSS `div > img` deixa de funcionar depois de activar Alter HTML | `replacement: "picture"` insere um elemento `<picture>` extra entre o `div` e o `img` | Usar selector `div img` (descendente) em vez de `div > img` (filho directo), ou mudar para `replacement: "url"` |
---
**Fonte:** `CONFIG-Plugins-Referencia.md` §5 + auditoria ao vivo original em
`emanuelalmeida.pt` + expansão nesta sessão com leitura completa do
código-fonte: `lib/classes/CLI.php`, `CachePurge.php`, `BulkConvert.php`, e
os 43 ficheiros `.inc`/`.php` de `lib/options/options/` (general,
conversion-options + converter-options/*.php, redirection-rules,
serve-options, alter-html, web-service-options, operation-mode.inc). Config
JSON completo (`config.<hash>.json`) lido ao vivo do disco — todas as
chaves de topo documentadas em §6 vêm directamente desse ficheiro, não só
das que a UI mostra por modo.