- emcp-page-building, emcp-content-ops, emcp-site-audit: EMCP Tools MCP workflows verified live (atomic/legacy interplay, apply-template overwrite risk, create-theme-template goes live immediately, false-positive malware pattern in scan-security, change ledger + rollback) - elementor-pro-widgets: 30+5 native Elementor Pro widgets (curated catalog) - elementskit-widgets / powerpack-widgets: 42 + 97 third-party widgets, widgetType extracted from plugin source (not guessed by convention) - plugin.json bumped 1.2.0 -> 1.3.0, keywords + description updated - commits pending wordpress skills already present as untracked files (emcp-tools, wordfence, wp-activity-log, seguranca-descomplicar, webp-express, wp-fastest-cache, wp-font-perf, wp-meteor, wp-activity-log, redis-object-cache, app-for-cloudflare) and pending edits (rank-math, wp-cli, wp-content-seo-gate)
44 KiB
name, description
| name | description |
|---|---|
| app-for-cloudflare | Gestão da zona Cloudflare a partir do wp-admin via App for Cloudflare® (`app-for-cf`, Digital Point) — SSL/TLS, Speed, Caching, Network, Scrape Shield, Bot Management, Super Bot Fight Mode, WAF real via Managed Ruleset, HSTS, Certificate Transparency, Leaked Credential Checks, Turnstile CAPTCHA, R2 media storage, Zero Trust Access, Page/Cache Rules, DMARC, Web Analytics, ferramentas de diagnóstico (request trace/IP/domain/WHOIS), purge de cache, guest page caching a nível de edge. Usar quando "app for cloudflare", "cloudflare wordpress", "zona cloudflare", "waf cloudflare", "bot management", "super bot fight mode", "hsts cloudflare", "rocket loader", "certificate transparency", "leaked credential checks", "bots de ia cloudflare", "ai_bots_protection", "is_robots_txt_managed", "managed ruleset", "purge cache cloudflare", "turnstile", "cloudflare r2", "cloudflare access", "page rules cloudflare", "cache rules cloudflare", "dmarc cloudflare", "cfPageCachingSeconds", "cfTurnstile", "app_for_cf", ou qualquer alteração a definições de zona Cloudflare/config do plugin num site do bundle Descomplicar. |
/app-for-cloudflare — Gestão de zona Cloudflare via App for Cloudflare®
Plugin app-for-cf (Digital Point), versão 1.10.0 (APP_FOR_CLOUDFLARE_VERSION),
instalado nos sites do bundle Descomplicar®.
Não é um simples proxy visual — a maior parte das definições de "boas
práticas" que o plugin lista não vivem em wp_options: são lidas e
escritas directamente na Cloudflare API v4, usando um token scoped
guardado numa única opção serializada. Confundir as duas camadas é o erro
mais comum ao operar este plugin.
Fonte: CONFIG-Plugins-Referencia.md §1 (App for Cloudflare®) e
BUNDLE-Excelencia-WP.md §2.2 e §2.4 (GEO/bots de IA) — mapeamento inicial
feito em emanuelalmeida.pt (16-08-2026), zona 574b243ebabd8ea2b8d89e68921f52cc,
plano Free Website. Expandido na mesma sessão com leitura completa do
código-fonte (11.309 linhas em 61 ficheiros PHP,
wp-content/plugins/app-for-cf/): Repository/Cloudflare.php (2178
linhas — getSettingsToManage(), setEasyMode(), updateSettings()),
Setup.php (defaults de wp_options), Admin/Template/Settings.php (766
linhas — UI completa das 6 tabs), Admin/Base/Admin.php (estrutura das 14
páginas de menu), Base/Pub.php (657 linhas — guest page caching, purge,
preload), Helper/Api.php (gate Free/Pro), Cron/Jobs.php e
Cli/PurgeCache.php (automação de purge), e todos os 14 templates de
página em Admin/Template/.
0. As duas camadas de config — não confundir
| Camada | Onde vive | Como ler/escrever | Exemplos |
|---|---|---|---|
| Config do plugin (não é Cloudflare) | wp_options.app_for_cf, array PHP serializado, 18 chaves (ver §8) |
wp option get/patch |
cfZoneId, cfAccountId, cloudflareAuth.token, cfPageCachingSeconds, cloudflarePreload, cfPurgeCacheOnAdminBar, cfTurnstile, cfProxy, cfR2Bucket |
| Config de zona Cloudflare (as ~52 "boas práticas" + Bot Management + WAF, ver §1) | Na Cloudflare, não no WordPress | API v4 (https://api.cloudflare.com/client/v4), bearer token = cloudflareAuth.token da opção acima |
ssl, security_header (HSTS), waf, bot_management, rocket_loader, cache_level, hotlink_protection, etc. |
Confirmado por leitura completa do código: não existe namespace WP REST
próprio exposto pelo plugin (app-for-cf não aparece em rest_api_init em
nenhum dos 61 ficheiros PHP) — todas as chamadas de configuração de zona
são directas à Cloudflare API v4 a partir do backend PHP do plugin
(Api/Cloudflare.php), usando o token guardado localmente. Para
automatizar fora do wp-admin, replicar essa mesma chamada directa
(curl/PHP) em vez de procurar um endpoint local que não existe.
A opção cloudflareAuth suporta dois modos de autenticação (ver
Setup::defaults()): type: 'token' (API token scoped, o único usado nos
sites do bundle) ou o modo legado email + api_key (Global API Key —
não usar, é o modo antigo e menos seguro da Cloudflare, mantido só por
compatibilidade retroativa no plugin).
Ler a config do plugin (wp-cli)
PATH=/home/USER/public_html
# Opção completa (JSON)
wp option get app_for_cf --format=json --allow-root --path=$PATH
# Uma sub-chave
wp option pluck app_for_cf cfZoneId --allow-root --path=$PATH
wp option pluck app_for_cf cfPageCachingSeconds --allow-root --path=$PATH
# Editar uma sub-chave sem apagar as restantes (SEMPRE patch, nunca update, em opção serializada)
wp option patch update app_for_cf cfPageCachingSeconds 21600 --allow-root --path=$PATH
wp option patch update app_for_cf cloudflarePreload 1 --allow-root --path=$PATH
cfProxy, cfR2Bucket, cloudflareAuth, cfTurnstile são sub-arrays —
wp option patch só edita um nível; para sub-arrays aninhados usar
wp eval com update_option() sobre o array completo lido primeiro.
Extrair o token para chamar a Cloudflare API directamente
TOKEN=$(wp eval 'echo maybe_unserialize(get_option("app_for_cf"))["cloudflareAuth"]["token"];' --allow-root --path=$PATH)
ZONE=$(wp eval 'echo maybe_unserialize(get_option("app_for_cf"))["cfZoneId"];' --allow-root --path=$PATH)
curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/settings" | jq .
1. As definições de zona (por categoria) — lista completa do código
Fonte: Repository/Cloudflare.php::getSettingsToManage() (linhas 88-721),
a definição autoritativa usada pelo próprio plugin para desenhar a página
de Settings e para updateSettings(). Achado desta expansão: o número
"44" da auditoria original referia-se à contagem informal mostrada na UI
(que agrupa alguns campos); a contagem real de entradas no array
getSettingsToManage() é 52. Lidas em bloco via GET /zones/{zone}/settings (a maioria) + endpoints individuais para os campos
com override_endpoint (security_header, certificate_transparency,
leaked_credential_checks, bot_management, speed_brain, fonts,
content_converter, origin_max_http_version, h2_prioritization,
settings/nel, flags para crawlhints, argo/tiered_caching).
Escritas via PATCH /zones/{zone}/settings/{setting_id} com {"value": ...}, excepto onde a definição tem overwrite_write_method diferente
(PUT para tudo em bot_management, POST para
leaked-credential-checks/ct/alerting/crawlhints).
| Categoria | Definições |
|---|---|
| Topo (fora de secção) | development_mode (bool, bom = off), security_level (select: off/essentially_off/low/medium/high/under_attack, bom = essentially_off) — mostradas no topo da página Settings, não numa tab |
| SSL/TLS | ssl (modo de encriptação: off/flexible/full/strict/origin_pull), always_use_https, security_header (HSTS — ver §3), min_tls_version (1.0-1.3), opportunistic_encryption, tls_1_3 (radio: off/on/zrt — zero round trip), automatic_https_rewrites, ech (Encrypted Client Hello), certificate_transparency (ver §4, beta), tls_client_auth |
| Segurança | waf (toggle legado, deprecated — ver §2), bot_fight_mode (Bot Fight Mode básico — ver §5a), ai_bots_protection, crawler_protection (ambos geridos via bot_management PUT — ver §5a), bot_likely_automated, bot_definitely_automated, bot_verified_bots, bot_static_resource_protection, bot_optimize_wordpress, bot_enable_js (Super Bot Fight Mode — ver §5a), leaked_credential_checks (ver §4), challenge_ttl (select 5min-12meses, bom = 30min), browser_check, replace_insecure_js |
| Speed | polish (select: off/lossless/lossy), webp, speed_brain (beta, substitui Rocket Loader/Mirage antigos), fonts (beta, Cloudflare Fonts — serve Google Fonts a partir do edge), early_hints, rocket_loader (deprecated para segurança — ver §2), content_converter (beta), http2, origin_max_http_version (int, bom = 2), http3, h2_prioritization, 0rtt (TLS 1.3 zero round trip) |
| Caching | cache_level (radio: basic/simplified/aggressive, bom = aggressive), browser_cache_ttl (select 0-12meses, 0 = respeitar headers de origem), crawlhints (beta, indica a crawlers quando o conteúdo mudou), always_online, tiered_caching (Argo Tiered Cache) |
| Network | ipv6, websockets, pseudo_ipv4 (select: off/add_header/overwrite_header), ip_geolocation, max_upload (select 100-500MB), nel (Network Error Logging, value.enabled aninhado), opportunistic_onion |
| Scrape Shield | email_obfuscation, hotlink_protection |
# Ler uma definição individual
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/settings/rocket_loader" | jq .
# Escrever uma definição individual (formato standard "value")
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/settings/rocket_loader" \
-d '{"value":"off"}' | jq .
webp, content_converter, speed_brain, fonts, crawlhints,
tiered_caching e outros podem estar plan-locked (não editáveis no
plano Free Website) — a API devolve o valor actual mas ignora tentativas
de escrita; não é bug, é limite de plano. A UI do plugin marca isto
lendo editable do resultado da API (ver override_result_editable_if_has
no código) e desactiva o <select>/checkbox correspondente.
EasyConfig — o que o botão "Easy config" aplica em bloco
Repository\Cloudflare::setEasyMode() (linha 723) aplica de uma vez, via
POST /zones/{zone}/settings (bulk) + 5 chamadas individuais:
0rtt=on, browser_cache_ttl=0, cache_level=aggressive, early_hints=on,
http3=on, ip_geolocation=on, ipv6=on, min_tls_version=1.2,
opportunistic_encryption=on, opportunistic_onion=on, pseudo_ipv4=off,
rocket_loader=off, tls_1_3=zrt, websockets=on,
security_level=essentially_off (fora do WordPress; 'medium' fora do XF)
+ settings/nel → {"value":{"enabled":false}}
+ settings/origin_max_http_version → {"value":"2"}
+ settings/speed_brain → {"value":"on"}
+ settings/fonts → {"value":"on"}
+ argo/tiered_caching → {"value":"on"}
Não toca em SSL/TLS (excepto min_tls_version/opportunistic_encryption),
Bot Management, WAF, HSTS nem Scrape Shield — o EasyConfig é só Speed +
Caching + Network básico. Preferir aplicar definição a definição (§7 desta
skill) para manter controlo e rasto de decisão, como já recomendado no
achado original.
2. WAF real — o toggle waf está morto, usar Managed Ruleset
O toggle legado waf está deprecated para zonas novas: PATCH /zones/{id}/settings/waf devolve 1027 WAF is deprecated for this zone
independentemente do valor enviado. O mecanismo real de protecção é a fase
de ruleset http_request_firewall_managed, que pode estar completamente
vazia (zero regras, zero protecção) mesmo num site "protegido pelo
Cloudflare" — foi o estado encontrado em emanuelalmeida.pt antes da
correcção.
# 1. Confirmar se a fase managed já tem entrypoint com regras
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/rulesets/phases/http_request_firewall_managed/entrypoint" | jq .
# Vazio/erro "could not find entrypoint" = zero protecção real, apesar do que o toggle `waf` sugere
# 2. Implantar o Cloudflare Managed Free Ruleset na fase managed
curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/rulesets/phases/http_request_firewall_managed/entrypoint" \
-d '{
"rules": [
{
"action": "execute",
"action_parameters": {
"id": "77454fe2d30c4220b5701f6fdfb893ba"
},
"expression": "true",
"description": "Cloudflare Managed Free Ruleset"
}
]
}' | jq .
77454fe2d30c4220b5701f6fdfb893ba é o ID do Cloudflare Managed Free
Ruleset — disponível em qualquer plano, incluindo Free Website. Testar
sempre com um pedido real (POST/AJAX contra o site) depois de implantar,
não confiar só no 200 OK do PUT.
A fase http_request_firewall_custom (regras próprias, não geridas) fica
tipicamente vazia (10003 could not find entrypoint) num site sem regras
custom — não é erro, é "zero regras próprias, só o Managed Ruleset". O
plugin (versão Pro) gere regras custom através da página Firewall
(§10) — "User agents" e "IP addresses" são interfaces amigáveis para criar
regras Custom Rules na fase http_request_firewall_custom sem precisar de
escrever expressões Cloudflare manualmente.
Rocket Loader não substitui isto. rocket_loader é uma definição de
Speed (adia/reordena <script> no browser), não de segurança — o
próprio plugin marca-o not_good_alert (recomenda desligado). Testado
nesta sessão como hipótese de causa de CLS elevado num site: desligar não
resolveu o CLS, mas manter desligado continua a ser a recomendação do
plugin por outras razões (risco de quebra em sites com jQuery/Elementor).
speed_brain é o sucessor recomendado pela Cloudflare (substitui Rocket
Loader e Mirage), mas está marcado beta no código do plugin.
3. HSTS (security_header)
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/settings/security_header" \
-d '{
"value": {
"strict_transport_security": {
"enabled": true,
"max_age": 15552000,
"include_subdomains": true,
"preload": false,
"nosniff": true
}
}
}' | jq .
Gotcha real desta sessão: se a origem (nginx) já envia o seu próprio
header Strict-Transport-Security, o HSTS do Cloudflare não o sobrepõe
— é comportamento documentado, não bug. Confirmar com curl -sI que o
cf-cache-status do pedido é MISS/DYNAMIC (não é cache stale) e que o
max-age devolvido é o da origem, não o configurado no Cloudflare. Ajustar
o max_age a partir da origem (nginx) quando o objectivo for realmente
mudar o valor visto pelo browser.
preload: false é deliberado por omissão — entrar na lista de preload dos
browsers é quase irreversível (remoção demora meses/anos a propagar). Só
activar depois de HSTS estável em produção há semanas sem incidentes.
O "bom" (good) definido no código do plugin para este campo é
max_age: 31536000 (12 meses), include_subdomains: 1, preload: 1,
nosniff: '' (vazio, i.e. não activa X-Content-Type-Options via este
mecanismo) — mais agressivo do que o valor sugerido na correcção original
desta skill (6 meses). Escolher em função do risco de subdomínios não
cobertos por HTTPS antes de ir para 12 meses + preload.
4. Certificate Transparency e Leaked Credential Checks
Endpoints próprios, fora do bulk settings:
# Certificate Transparency (alertas quando um certificado é emitido para o domínio)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/ct/alerting" \
-d '{"enabled": true}' | jq .
# Leaked Credential Checks (avisa se credenciais usadas no site apareceram em leaks conhecidos)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/leaked-credential-checks" \
-d '{"enabled": true}' | jq .
Ambos vinham off por omissão num site com anos de conta Cloudflare — não
assumir que "conta antiga = tudo activo por defeito"; a Cloudflare tem
adicionado features de segurança ao longo do tempo sem as retroactivar em
zonas já existentes.
5. Bot Management (endpoint /bot_management) — 7 campos + is_robots_txt_managed
Endpoint próprio: GET/PUT /zones/{zone}/bot_management (não faz parte
do bulk settings, e não aceita PATCH). Estes campos foram
verificados via chamada directa à API (não são geridos pela UI do plugin
através de getSettingsToManage(), ao contrário dos campos do §5a) — o
plugin não expõe content_bots_protection, ai_training, ai_search,
ai_user nem is_robots_txt_managed na sua própria página de Settings,
mas estes são campos reais e válidos do endpoint bot_management da
Cloudflare, geríveis por chamada directa à API.
| Campo | Padrão óptimo Descomplicar® | O que faz mal se ligado |
|---|---|---|
ai_bots_protection |
disabled |
Bloqueia crawlers de IA (GPTBot, ClaudeBot, etc.) — visto block activo em 2 sites do bundle |
content_bots_protection |
disabled |
Idem, granularidade diferente |
crawler_protection |
disabled |
Bloqueia crawlers genéricos |
ai_training |
disabled |
Bloqueia bots de treino de modelos |
ai_search |
disabled |
Bloqueia bots de motores de busca IA |
ai_user |
disabled |
Bloqueia bots accionados por utilizador de IA (ex. ChatGPT a navegar em nome do utilizador) |
fight_mode |
false |
Desafia (challenge JS) tráfego automatizado por heurística — apanha crawlers legítimos de IA que não resolvem JS challenges, visto a devolver "Just a moment..." em vez de conteúdo real |
Achado real desta sessão (GEO): com is_robots_txt_managed: true, o
Cloudflare injecta directivas Disallow: / no robots.txt servido
para estes user-agents, por cima do ficheiro físico do site — mesmo que o
ficheiro físico já permita esses bots explicitamente. Parsers que fundem
grupos do mesmo user-agent aplicam o Disallow na mesma, tornando a
permissão do ficheiro físico letra morta.
# Ler estado actual
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/bot_management" | jq .
# Corrigir — PUT, NUNCA PATCH (PATCH devolve sempre 10405 Method not allowed,
# independentemente das permissões do token)
curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/bot_management" \
-d '{
"ai_bots_protection": "disabled",
"content_bots_protection": "disabled",
"crawler_protection": "disabled",
"ai_training": "disabled",
"ai_search": "disabled",
"ai_user": "disabled",
"fight_mode": false,
"is_robots_txt_managed": false
}' | jq .
Depois de corrigir, purgar cache (Cloudflare + cache local WP, ex. WP
Fastest Cache) — robots.txt costuma ter cache-control: max-age=315360000, a correcção não aparece sem purge. Confirmar ao vivo
com curl -sI que cf-cache-status não é HIT e o corpo já não tem
Disallow: / para os bots visados.
5a. Super Bot Fight Mode — campos geridos pela própria UI do plugin
Ao contrário do §5 (chamada directa, campos fora da UI), estes 8 campos
são geridos por getSettingsToManage() e aparecem na tab "Security"
da página Settings do plugin. Todos escrevem no mesmo endpoint PUT /bot_management, mas com nomes de campo JSON diferentes do que o id da
definição no plugin:
| ID no plugin | Campo real na API bot_management |
Bom (good) |
|---|---|---|
bot_fight_mode |
fight_mode |
false |
ai_bots_protection |
ai_bots_protection |
block (nota: o good do código é block, oposto do padrão Descomplicar do §5 — ver decisão abaixo) |
crawler_protection |
crawler_protection |
enabled (idem — oposto do padrão Descomplicar) |
bot_likely_automated |
sbfm_likely_automated (select allow/block/managed_challenge) |
allow |
bot_definitely_automated |
sbfm_definitely_automated (select allow/block/managed_challenge) |
allow |
bot_verified_bots |
sbfm_verified_bots (select allow/block) |
allow |
bot_static_resource_protection |
sbfm_static_resource_protection (bool) |
false |
bot_optimize_wordpress |
optimize_wordpress (bool) |
false |
bot_enable_js |
enable_js (bool) — comentário no código: "a API diz que é editável, mas só é possível de facto em plano Pro ou superior" |
false |
Contradição intencional a documentar: o good que o código do plugin
define para ai_bots_protection/crawler_protection é o valor "block"/
"enabled" (i.e. o próprio autor do plugin recomenda bloquear bots de IA
por omissão). A decisão Descomplicar® (documentada no §5 e no gotcha da
tabela §7) é o oposto — nunca bloquear bots de IA em nenhum site do
bundle, por ser GEO-crítico (visibilidade em AI Overviews/ChatGPT/
Perplexity). Ao corrigir zonas, ignorar deliberadamente o "good" do plugin
para estes 2 campos específicos e aplicar sempre disabled.
Todas as escritas neste grupo enviam unset_on_write: ['using_latest_model']
— o código remove explicitamente esse campo do payload antes de fazer
PUT, porque using_latest_model é campo só de leitura devolvido pela API
(indica se a Cloudflare está a usar o modelo de detecção de bots mais
recente) e a API rejeita o PUT se ele for reenviado.
6. Scopes de token — lista completa (21 permissões, do código de setup)
Admin/Template/MultisiteSettings.php e Admin/Template/Settings.php
embutem a lista completa e oficial de scopes recomendados pelo autor do
plugin (usada para construir o link de criação de token pré-preenchido no
dashboard Cloudflare) — muito mais granular do que os 6 grupos informais
documentados na versão anterior desta skill:
| Scope Cloudflare | Nível de acesso | Necessário para |
|---|---|---|
Account.Access: Apps and Policies |
Edit | Zero Trust Access (página Access, Pro) |
Account.Access: Organizations, Identity Providers, and Groups |
Read | Zero Trust Access — resolver grupos/IdPs nas políticas |
Account.Account Analytics |
Read | Dados agregados de conta (não usado nesta skill) |
Account.Account Settings |
Read + Edit | Detalhes/definições de conta Cloudflare |
Account.Allow Request Tracer |
Read | Página "HTTP request trace" (§10) |
Account.Billing |
Read | Info de plano/facturação Cloudflare mostrada no plugin |
Account.Intel |
Read | Páginas "IP address details"/"Domain details"/"WHOIS" (Cloudflare Intel API, §10) |
Account.Turnstile |
Edit | Criar/gerir sitekeys Turnstile CAPTCHA (§8, cfTurnstile) |
Account.Workers R2 Storage |
Edit | R2 media storage (página R2, Pro) |
Account.Workers Scripts |
Edit | Worker proxy (cfWorkersSubdomain/cfProxy, §8) |
User.API Tokens |
Read | O plugin lê as próprias permissões do token para colorir a checklist de setup a verde |
Zone.Analytics |
Read | Web Analytics / dashboards de tráfego |
Zone.Bot Management |
Edit | Bot Management + Super Bot Fight Mode (§5, §5a) — separado do Zone Settings, falta frequentemente no token gerado por omissão |
Zone.Cache Purge |
Purge | Purge de cache (página "Purge cache", botão admin bar) |
Zone.Cache Rules |
Edit | Cache Rules (página Rules, Pro) |
Zone.Firewall Services |
Edit | Firewall Rules, regras de User-Agent/IP (página Firewall, Pro) |
Zone.Page Rules |
Edit | Page Rules legadas (página Rules, Pro) |
Zone.SSL and Certificates |
Edit | Certificate Transparency, TLS client auth |
Zone.Zone |
Edit | Operações de gestão da própria zona |
Zone.Zone Settings |
Edit | As ~52 definições de zona do §1 (SSL/TLS, Speed, Caching, Network, Scrape Shield) |
Zone.Zone WAF |
Edit | WAF/Managed Ruleset (§2) |
Zone.DNS não está na lista oficial do plugin — o plugin não gere DNS
(DMARC/SPF/DKIM são lidos como registos públicos via dig, ver §7/§10, não
via API Cloudflare autenticada).
Sintoma de scope em falta: a API devolve 403 ou o próprio plugin
mostra um erro de permissão específico à operação, não um erro genérico de
autenticação — o token pode estar válido e ainda assim faltar-lhe
exactamente o scope daquela chamada. Testado nesta sessão: token com Zone SSL/Settings Edit funcionava para as definições de zona mas falhava em
Bot Management até se alargar explicitamente o scope no dashboard
Cloudflare (My Profile → API Tokens → editar token).
7. Gotchas e erros comuns
| Sintoma | Causa | Solução |
|---|---|---|
PATCH /zones/{id}/settings/waf → 1027 WAF is deprecated for this zone |
Toggle legado morto para zonas novas | Implantar Managed Ruleset na fase http_request_firewall_managed (§2) |
PATCH /zones/{id}/bot_management → 10405 Method not allowed |
Endpoint só aceita PUT |
Usar PUT sempre, mesmo para alterar um único campo (envia o objecto completo) |
Correcção de robots.txt/HSTS não aparece no browser |
Header/ficheiro servido a partir de cache (Cloudflare e/ou plugin de cache WP) | Purgar Cloudflare + cache WP local; confirmar cf-cache-status não é HIT antes de validar |
HSTS configurado no Cloudflare mas browser continua a ver max-age diferente |
Origem (nginx) já envia o seu próprio header Strict-Transport-Security — Cloudflare não sobrepõe HSTS que a origem já envia |
Ajustar na origem, ou aceitar que o Cloudflare é só fallback para origens sem HSTS próprio |
Regra de firewall/Transform Rule com operador matches (regex) falha ou não fica disponível na UI |
matches exige plano Business (WAF Advanced) |
Reescrever a expressão com in, eq ou contains — disponíveis em qualquer plano, incluindo Free |
wp option update app_for_cf '...' apaga sub-chaves que não estavam no valor enviado |
wp option update substitui a opção inteira; app_for_cf é um array serializado |
Usar wp option patch update app_for_cf <chave> <valor> para editar uma sub-chave sem tocar nas restantes |
R2 (media storage) devolve 10042 Please enable R2 through the Cloudflare Dashboard |
R2 não está activado a nível de conta (não é definição de zona nem do plugin) | Activar manualmente no dashboard Cloudflare da conta antes de qualquer chamada à API de R2 |
ai_bots_protection/crawler_protection a block/enabled num site do bundle |
Divergência do padrão óptimo — visto em carstuff.pt e solarfvengenharia.com nesta sessão; o próprio good do código do plugin recomenda block (§5a), por isso corrigir manualmente sempre que se aplicar EasyConfig ou se seguir uma recomendação genérica do plugin |
Corrigir para disabled via PUT /bot_management (§5) — decisão explícita: nunca bloquear bots de IA em nenhum site Descomplicar |
| Página "Public page caching"/"Purge cache"/analytics parecem não fazer nada de imediato | handleHeaders() só define Cache-Control: max-age=0,s-maxage=X — depende do Cloudflare respeitar esse header (cache_level/browser_cache_ttl compatíveis) e de não haver cookies de sessão a bloquear o cache (ver §9) |
Confirmar browser_cache_ttl = "Respect existing headers" (0) e cache_level ≠ basic; testar em aba anónima sem cookies wp-*/wordpress_*/comment_*/woocommerce_* |
Botões "Firewall", "Access", "Rules" (criar regra), "R2", "Copy from...", "License key" aparecem desactivados/a cinzento ou com classe pro |
Funcionalidade gated para a versão Premium/Pro do plugin (Helper\Api::check() — ver §11) |
Confirmar licença activa (cfLicenseKey) ou aceitar a limitação no plano Free |
wp option get app_for_cf mostra cfTurnstile:["0"] em vez do array completo com siteKey/onLogin/etc. |
Turnstile nunca foi activado neste site — o checkbox "Use Turnstile CAPTCHA" desmarcado grava só [0] (o <input type="hidden" value="0"> do formulário), não o array completo de Setup::defaults() |
Normal em sites sem Turnstile configurado; a estrutura completa (13 sub-chaves, ver §8) só aparece depois de gerar uma sitekey e gravar o formulário |
8. Config do plugin (wp_options.app_for_cf) — as 18 chaves em detalhe
Fonte: Setup::defaults() (schema oficial de instalação) cruzado com a
leitura ao vivo (wp option get app_for_cf) em emanuelalmeida.pt
(16-08-2026) — as 18 chaves batem certo entre defaults e produção.
{"cfWorkersSubdomain":"","cfProxy":{"image":"0","url":"0"},"cfR2Bucket":{"media":""},"cfAccountId":"a8b00ad3227d729a146143a6a85a7522","cfZoneId":"574b243ebabd8ea2b8d89e68921f52cc","cfTokenId":"6b6d24fe8d324112ea4cfc328bf16f6c","cfZone":"emanuelalmeida.pt","cfPageCachingSeconds":"21600","cloudflareAuth":{"token":"cfut_..."},"cloudflareFirewallExpireDays":"7","cloudflareBlockIpsSpamClean":"0","cfExternalDataUrl":"","cloudflarePreload":"1","cfImagesTransform":"0","cfLicenseKey":"","cfPurgeCacheOnAdminBar":"1","cfTurnstile":["0"],"LockSettingsUserId":"1"}
| Chave | Tipo/Default | O que faz |
|---|---|---|
cfWorkersSubdomain |
string, '' |
Subdomínio *.workers.dev da conta Cloudflare, usado quando o Worker proxy (R2/imagens) está activo |
cfProxy.image / cfProxy.url |
0/1 cada |
Toggle interno para saber se URLs de imagem/media já estão a ser reescritas para servir via Worker/R2 (usado por filterWpGetAttachmentUrl()/filterWpCalculateImageSrcset() em Base/Pub.php) |
cfR2Bucket.media |
string, '' |
Nome do bucket R2 escolhido/criado para armazenar media da biblioteca WordPress (feature Pro, página R2) |
cfAccountId |
string | ID da conta Cloudflare — resolvido automaticamente por getZoneId() a partir do hostname na primeira chamada, cacheado aqui |
cfZoneId |
string | ID da zona Cloudflare — idem, cacheado para evitar listZones() a cada pedido |
cfTokenId |
string | ID do token API (não o valor secreto) — mostrado na UI como referência ("Token ID: ...") para o admin confirmar qual token está activo |
cfZone |
string | Nome do domínio da zona (ex. emanuelalmeida.pt), cacheado junto com cfAccountId/cfZoneId |
cfPageCachingSeconds |
int (segundos), '' (vazio = desligado) |
Tempo de s-maxage aplicado ao HTML para guests (§9) — 0/vazio desliga o guest page caching |
cloudflareAuth.type |
'token' ou 'email' |
Modo de autenticação — token é o único usado/recomendado; email+api_key é o modo legado Global API Key |
cloudflareAuth.token |
string secreta | Bearer token da Cloudflare API, scoped (ver permissões no §6) |
cloudflareAuth.email / cloudflareAuth.api_key |
string, '' |
Só usados se type = 'email' (não usar) |
cloudflareFirewallExpireDays |
int, default 7 (1-90) |
Dias até uma regra de firewall criada automaticamente pelo plugin (ex. bloqueio de IP de spammer, feature Pro) expirar sozinha |
cloudflareBlockIpsSpamClean |
0/1, default 1 |
Se 1, bloqueia automaticamente (feature Pro) o IP de um utilizador cujo comentário foi marcado como spam pela fila de moderação do WordPress |
cfExternalDataUrl |
string URL, '' |
URL pública onde o media fica acessível quando R2 está activo — normalmente auto-preenchido ao activar R2, não editar manualmente salvo troubleshooting |
cloudflarePreload |
0/1, default 1 |
Se 1, o plugin usa hooks script_loader_tag/style_loader_tag/wp_footer para emitir headers HTTP Link: <url>;rel=preload (até 10 recursos) — funciona em conjunto com a definição de zona early_hints |
cfImagesTransform |
0/1, default 0 |
Se 1, activa Cloudflare Images Transform para blocos de imagem/media (serve AVIF/WebP a browsers modernos, formatos mais antigos a browsers antigos, qualidade reduzida em ligações muito lentas) — requer activação também a nível de zona no dashboard Cloudflare |
cfLicenseKey |
string, '' |
Chave de licença da versão Premium/Pro do plugin — sem isto, Helper\Api::check() devolve sempre false (ver §11) |
cfPurgeCacheOnAdminBar |
0/1, default 0 |
Se 1, mostra um botão de purge de cache com um clique na admin bar do WordPress |
cfTurnstile |
array com 13 sub-chaves quando activo: siteKey, secretKey, onRegister, onLogin, onPassword, onComment, onContactForm7, onHtmlForms, onMetForm, onWPForms, onWooCommerceRegister, onWooCommerceLogin, onWooCommercePassword |
CAPTCHA Turnstile aplicado por contexto — cada onX é um toggle independente que decide se o widget Turnstile aparece nesse formulário específico (registo/login/reset password/comentários WP nativos, WooCommerce, e os plugins de formulário Contact Form 7/HTML Forms/MetForm/WPForms se estiverem instalados) |
LockSettingsUserId |
0 ou ID de utilizador WP |
Se preenchido com um ID de utilizador, só essa conta WordPress pode alterar as definições Cloudflare a partir do wp-admin — risco: apagar essa conta ou trocar de utilizador tranca o acesso às definições |
network_exclude (visto em código de multisite, Settings.php linha 290)
é uma chave adicional só relevante em instalações multisite — indica que
um site individual optou explicitamente por não herdar o token
API de rede.
9. Guest page caching (HTML a nível de edge) — como funciona de facto
Página "Public page caching" (§10) e a chave cfPageCachingSeconds (§8)
não usam Page Rules nem Cache Rules Cloudflare — usam um mecanismo mais
simples e mais portável: o hook wp_headers do WordPress
(Base\Pub::handleHeaders(), Base/Pub.php linha 276) define
Cache-Control: max-age=0,s-maxage=<cfPageCachingSeconds>
em toda resposta HTML elegível. max-age=0 diz ao browser para não
cachear localmente; s-maxage=<N> diz a caches partilhadas (o edge da
Cloudflare) para cachear durante N segundos — desde que a definição de
zona browser_cache_ttl esteja em 0 ("Respect Existing Headers", o
padrão recomendado no §1) e cache_level não seja basic.
Critérios de elegibilidade (pageCachingCriteria(), filtráveis via
app_for_cf_guest_page_cacheable):
- Utilizador não tem sessão WordPress activa (
is_user_logged_in()falso) - Nenhum cookie do browser começa por
wp-,wordpress_,comment_ouwoocommerce_(senão a página é tratada como dinâmica/pessoal e o header de cache não é aplicado) - Pedido não é para
/wp-login.php - Content-Type da resposta começa por
text/html - Página não é 404
WP_DEBUGnão está activo
Por omissão o valor sugerido na UI é 6 horas (21600 segundos, a mesma
opção default usada em CachingGuestPage.php); opções disponíveis vão
de 5 minutos a 24 horas, filtráveis via app_for_cf_cache_times, e o
limite máximo é derivado da vida do nonce do WordPress
(DAY_IN_SECONDS - 3600, tipicamente 23h).
# Ler/alterar o tempo de guest page caching
wp option pluck app_for_cf cfPageCachingSeconds --allow-root --path=$PATH
wp option patch update app_for_cf cfPageCachingSeconds 21600 --allow-root --path=$PATH # 6h
wp option patch update app_for_cf cfPageCachingSeconds '' --allow-root --path=$PATH # desligar
Como não é uma Page/Cache Rule, não aparece no dashboard Cloudflare
como regra — só é visível inspeccionando o header Cache-Control da
resposta HTTP (curl -sI https://SITE/) e o cf-cache-status.
10. As 14 páginas de admin — o que cada uma faz
Estrutura de menu confirmada em Admin/Base/Admin.php. O item de topo
(Cloudflare, ícone dashicons-cloud) mostra um badge ! quando não há
token configurado.
Slug (page=) |
Título no menu | O que faz |
|---|---|---|
app-for-cf_caching |
Public page caching | Toggle + selector de tempo para o guest page caching do §9 (cfPageCachingSeconds) |
app-for-cf_firewall |
Firewall | 3 tabelas: Firewall Rules (link "Create rule" abre o dashboard Cloudflare — Pro), User agents (regras de bloqueio por user-agent geridas inline — Pro) e IP addresses (bloqueio manual por IP — Pro); todas suportam bulk enable/disable/delete via bulkActionNotice() |
app-for-cf_access |
Access | Lista de Zero Trust Access Apps + Groups da conta, com delete em massa (feature Pro — cria políticas de acesso, ex. proteger /wp-admin com SSO) |
app-for-cf_rules |
Rules | 2 tabelas: Page Rules legadas (link "Create page rule" abre dashboard — Pro) e Cache Rules modernas (link "Create cache rule" — Pro) |
app-for-cf_r2 |
R2 (media storage) | Explica e liga a biblioteca de media do WordPress a um bucket Cloudflare R2 (10GB grátis, depois $0.015/GB); requer token válido e licença Pro |
app-for-cf_cache |
Purge cache | Botão único "Purge Cloudflare cache..." — chama purgeCache() sem filtro (Purge Everything) |
options-general.php?page=app-for-cf |
Settings (link para wp-admin/options-general.php?page=app-for-cf) |
A página principal com as 6 tabs de definições de zona (Setup/SSL-TLS/Security/Speed/Caching/Network+Scrape Shield) — ver §1; badge "Missing API token" se cloudflareAuth.token vazio |
app-for-cf_analytics |
Web analytics | Toggle único para injectar o beacon JavaScript do Cloudflare Web Analytics (privacy-first, sem cookies) em todas as páginas; link directo para o dashboard de analytics quando activo |
app-for-cf_dmarc |
DMARC management | Gráfico (Chart.js) de volume de email pass/fail por semana/mês + tabela de fontes de envio detectadas via os relatórios DMARC que a Cloudflare recebe em nome do domínio; link "View in Cloudflare" para o dashboard |
app-for-cf_request-trace |
HTTP request trace | Formulário para simular um pedido (URL, método, protocolo, bot score, país, threat score, skip challenge) e ver que produtos/regras Cloudflare seriam aplicados — requer scope Account.Allow Request Tracer |
app-for-cf_ip-details |
IP address details | Consulta a Cloudflare Intel API para um IP: PTR, ASN/organização, tipo, risk types — requer Account.Intel:Read |
app-for-cf_domain-details |
Domain details | Idem para domínios: tipo, IPs de resolução, categorias de conteúdo |
app-for-cf_whois |
WHOIS | Consulta WHOIS via Cloudflare Intel: datas de criação/actualização, registrant, registrar, nameservers |
| (sem entrada de menu própria) Copy Settings | acedida a partir de um botão na página Settings | Copia todas as definições de zona de uma zona diferente da mesma conta Cloudflare para a zona actual — irreversível, exige confirmação explícita ("I understand...") antes de activar o botão de submissão |
Páginas exclusivas de multisite network admin (settings.php?page=):
| Slug | Título | O que faz |
|---|---|---|
app-for-cf_multisite-settings |
Cloudflare (em Network Admin → Settings) | Define um token API partilhado por toda a rede (app_for_cf_network), com a mesma checklist de 21 permissões do §6; cada site individual pode optar por usar o token de rede ou o seu próprio |
app-for-cf_multisite-r2 |
R2 (media storage) (Network Admin) | Um único bucket R2 partilhado por todos os sites da rede, com o caminho de cada ficheiro prefixado por {siteId}/ (ex. cdn.site.com/50/2023/10/image.png) — feature Pro, licenciada ao hostname do site principal da rede |
11. Free vs Pro — gate Helper\Api::check()
Helper\Api::$version / Helper\Api::check($force = false) (Helper/Api.php)
determina se a licença Premium está activa através de um transient
(acf_int) com 6 horas de validade (21600 segundos) — não é lido de
cfLicenseKey directamente a cada pedido, é cacheado. Setup::install()
chama Api::check(true) (forçado) na activação do plugin.
Funcionalidades bloqueadas/degradadas no plano Free (confirmado nos
templates admin, condicional Helper\Api::$version ou classe CSS pro):
- Botão "Create rule"/"Create page rule"/"Create cache rule"/"Create user agent rule"/"Create IP address rule" (Firewall, Rules) — sem eles, as tabelas continuam a listar regras já criadas manualmente no dashboard Cloudflare, só não permitem criar a partir do wp-admin
- Página R2 (media storage) — bloqueada mesmo com token válido
- Página Access (Zero Trust) — lista mas não gere
- "Copy from..." (Copy Settings) — botão só aparece se
Helper\WordPress::hasOwnApiToken()e implicitamente exige Pro para ser útil em produção - Campo "License key" só aparece se
Helper\Api::$versionjá tiver alguma licença associada (mostra o campo para trocar/renovar) - "Block spammer IPs"/"Days that firewall rules last"/"External data
URL" (
cloudflareBlockIpsSpamClean,cloudflareFirewallExpireDays,cfExternalDataUrl) aparecem com classe CSSproedisabledquando(int)$cloudflareAppInternalé falso — os campos ficam visíveis mas não editáveis
Todas as definições de zona do §1 (SSL/TLS, Speed, Caching, Network, Scrape Shield, Bot Management/SBFM), o WAF via Managed Ruleset (§2), HSTS (§3), Certificate Transparency/Leaked Credential Checks (§4), guest page caching (§9), purge de cache e Turnstile CAPTCHA (§8) funcionam no plano Free do plugin — não são gated, só dependem do plano Cloudflare subjacente (Free Website vs Pro/Business), que é uma dimensão diferente.
12. Automação — WP-CLI e Cron
# Purgar cache Cloudflare (Purge Everything) via linha de comandos
wp app-for-cf purge-cache --allow-root --path=$PATH
Implementado em Cli/PurgeCache.php (comando app-for-cf purge-cache,
@when after_wp_load) — chama Repository\Cloudflare::purgeCache() sem
argumentos (equivalente ao botão da página "Purge cache", §10).
Cron\Jobs::purgeCache(array $urls) é o mecanismo interno usado por
Base\Pub::purgeCacheByPostIds() quando um post muda de estado
(publicado/editado) ou um comentário muda de estado — faz chunking em
lotes de 30 URLs por chamada à API de purge (limite da Cloudflare por
pedido), parando o loop no primeiro chunk que falhar em vez de continuar a
tentar os restantes.
13. Áreas do plugin sem config a auditar/activar (confirmado nesta sessão, emanuelalmeida.pt)
| Área | Estado |
|---|---|
| Cache Rules / Page Rules | 0 regras — GET /zones/{id}/pagerules → count:0 |
| Access (Zero Trust) | Não configurado, fora de âmbito nesta sessão — funcionalidade real do plugin, ver §10 |
| Turnstile CAPTCHA | Site key existe mas desligada no plugin (cfTurnstile: ["0"]) — infra pronta se decidido substituir reCAPTCHA; ver estrutura completa das 13 sub-chaves no §8 |
Workers proxy (cfProxy) |
{image: off, url: off} |
| R2 (media storage) | cfR2Bucket.media vazio, cfExternalDataUrl vazio — nunca activado neste site; requer licença Pro (§11) |
DMARC/SPF/DKIM (Dmarc.php) |
Auditar via DNS público directo (dig TXT), nunca precisa de token Cloudflare — são registos públicos por desenho; a página DMARC do plugin (§10) é sobre os relatórios agregados que a Cloudflare recebe, não sobre editar os próprios registos DNS |
| Web Analytics (beacon JS) | Confirmar por ausência/presença de cloudflareinsights/beacon.min.js no HTML servido, não por option — a página Web Analytics (§10) tem toggle próprio fora de wp_options (via API, não confirmado nesta sessão se lê de zona ou de conta) |
EasyConfig (setEasyMode()) |
Aplica tudo em bloco (lista exacta no §1) — preferir aplicar definição a definição para manter controlo e rasto de decisão |
Licença Pro (cfLicenseKey) |
Vazia — plugin a correr em modo Free; ver §11 para o que fica indisponível |
Fonte
CONFIG-Plugins-Referencia.md §1 (App for Cloudflare®) — achados originais
de HSTS/WAF/DMARC, credenciais de sessão. BUNDLE-Excelencia-WP.md §2.2
(Performance) e §2.4 (GEO — bots de IA, achado completo do
is_robots_txt_managed e dos 7 campos de Bot Management via API directa).
Ambos em Hub/04-Stack/02.04-Sistemas/71.Seguranca/, piloto
emanuelalmeida.pt, sessão 16-08-2026.
Expansão desta sessão (16-08-2026): leitura completa do código-fonte
do plugin app-for-cf v1.10.0 via SSH (server, 61 ficheiros PHP,
11.309 linhas) — não apenas os ficheiros tocados na auditoria pontual
original. Ficheiros lidos na íntegra: app-for-cf.php (bootstrap),
Setup.php (schema de wp_options), Repository/Cloudflare.php (2178
linhas — lista completa de 52 definições de zona + setEasyMode() +
lógica de leitura/escrita), Admin/Template/Settings.php (766 linhas —
UI completa, scopes de token, Turnstile), Admin/Base/Admin.php (menu,
14 páginas), Helper/Api.php (gate Free/Pro), Cron/Jobs.php,
Cli/PurgeCache.php, e os 14 templates individuais em Admin/Template/
(Firewall.php, R2.php, Access.php, Rules.php, Analytics.php,
Dmarc.php, RequestTrace.php, IpDetails.php, DomainDetails.php,
Whois.php, CachingGuestPage.php, Caching.php, Cache.php,
CopySettings.php, MultisiteR2.php, MultisiteSettings.php) e trechos
relevantes de Base/Pub.php (657 linhas — guest page caching, preload,
purge). Confirmado ao vivo com wp option get app_for_cf --format=json
em emanuelalmeida.pt que as 18 chaves de wp_options batem certo com
Setup::defaults(). Nenhuma escrita feita em produção durante esta
expansão — só leitura de código-fonte e wp option get.