---
name: rank-math
description: Gestão programática do Rank Math SEO (Free e PRO) via WP-CLI em servidores CWP. Cobre auditoria SEO, meta em massa, schema markup, redirects, sitemaps, Instant Indexing, Analytics PRO, hooks/filtros, taxonomias WooCommerce, News/Video Sitemap, validação e rollback. Usar quando "rank math", "seo cli", "schema markup", "redirects seo", "instant indexing", "indexnow", "seo bulk", "rankmath pro".
---

# /rank-math — Gestão Programática Rank Math SEO via WP-CLI

Operações SEO WordPress completas via WP-CLI no servidor CWP. Free + PRO.

**Manual completo:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI/Rank-Math-WP-CLI-Manual-Definitivo.md`
**Quick Reference:** `Hub/06-Operacoes/Documentacao/Quick-Reference/QR-RankMath-WP-CLI.md`
**Pesquisa Claude:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI/Rank-Math-SEO-WP-CLI-Skils-Pesquisa-Claude.md`
**Pesquisa Gemini:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI/Rank-Math-SEO-WP-CLI-Skills-Pesquisa-Gemini.md`
**NotebookLM:** [WordPress Config CLI](https://notebooklm.google.com/notebook/fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d)
**Skill relacionada:** `/seo-post` — optimizar posts individuais com pipeline completo

---

## Contexto NotebookLM

ANTES de executar, consultar notebooks para contexto especializado:

| Notebook | ID | Consultar quando |
|----------|-----|-----------------|
| WordPress e Elementor | 5be0d1a6 | Sempre |
| Marketing Digital PT | 4c595973 | Para contexto SEO |

```
mcp__notebooklm__notebook_query({
  notebook_id: "5be0d1a6-00f2-4cd9-b835-978cb7721601",
  query: "<adaptar ao contexto do pedido do utilizador>"
})
```

---

## Uso

```
/rank-math audit SITE_PATH
/rank-math update-meta SITE_PATH
/rank-math sitemap SITE_PATH
/rank-math redirects SITE_PATH
/rank-math schema post ID SITE_PATH
/rank-math indexing SITE_PATH [URL|bulk]
/rank-math analytics SITE_PATH
/rank-math taxonomy SITE_PATH
/rank-math hooks SITE_PATH
/rank-math backup SITE_PATH
/rank-math provision SITE_PATH
```

---

## Contexto CWP — sempre obrigatorio

```bash
# Formato base (PHP 8.3)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp <comando> --allow-root --path=/home/USER/public_html

# Alias
wp83 <comando> --allow-root --path=/home/USER/public_html
```

**Acesso via MCP SSH:**
```
servidor: server.descomplicar.pt (176.9.3.158) | porta: 9443 | user: root
```

**Prefixo de tabelas:** Sites podem ter prefixo custom (ex: `wpah_` em vez de `wp_`). Detectar primeiro:
```bash
PREFIX=$(wp db prefix --allow-root --path=$PATH)
# Usar $PREFIX em todas as queries SQL: ${PREFIX}posts, ${PREFIX}postmeta, etc.
```

**Unico comando nativo Rank Math:**
```bash
wp rankmath sitemap generate --allow-root --path=$PATH
```

Tudo o resto usa `wp post meta`, `wp option patch`, `wp eval` ou `wp db query`.

**Options com defaults:** Muitas sub-keys (title_separator, strip_category_base, homepage_title, knowledgegraph_type) podem nao existir na BD se nunca foram alteradas — o Rank Math usa valores por defeito internos. `wp option pluck` retorna exit code 1 neste caso. Nao e erro — significa valor default.

---

## Decision tree — qual comando usar

| Operacao | Comando |
|----------|---------|
| Alterar meta de 1 post | `wp post meta update` |
| Alterar sub-key em option serializada | `wp option patch update` |
| Ler sub-key de option | `wp option pluck` |
| Schema complexo | `wp eval` com PHP |
| Redirects | `wp eval` com `\RankMath\Redirections\DB` |
| Bulk simples | shell loop com `wp post list \| while read` |
| Bulk complexo | `wp eval-file` com script PHP |
| SQL directo | apenas auditorias (seguido de `wp cache flush`) |
| Regenerar sitemap | `wp rankmath sitemap generate` |
| Instant Indexing | `wp eval` com IndexNow API ou classes RM |
| Side-effects RM | `wp eval` com `update_option()` (dispara hooks) |

---

## 1. Auditoria SEO (/rank-math audit)

### Passo 1 — diagnostico rapido

```bash
PATH=/home/USER/public_html
PREFIX=$(wp db prefix --allow-root --path=$PATH)

# Posts sem titulo SEO
wp db query "SELECT p.ID, p.post_title FROM ${PREFIX}posts p
LEFT JOIN ${PREFIX}postmeta pm ON p.ID=pm.post_id AND pm.meta_key='rank_math_title'
WHERE p.post_type='post' AND p.post_status='publish'
AND (pm.meta_value IS NULL OR pm.meta_value='');" --allow-root --path=$PATH

# Posts sem descricao SEO
wp db query "SELECT p.ID, p.post_title FROM ${PREFIX}posts p
LEFT JOIN ${PREFIX}postmeta pm ON p.ID=pm.post_id AND pm.meta_key='rank_math_description'
WHERE p.post_type='post' AND p.post_status='publish'
AND (pm.meta_value IS NULL OR pm.meta_value='');" --allow-root --path=$PATH

# Distribuicao de SEO scores
wp db query "SELECT
  CASE WHEN CAST(meta_value AS UNSIGNED) >= 80 THEN 'Bom (80-100)'
       WHEN CAST(meta_value AS UNSIGNED) >= 50 THEN 'OK (50-79)'
       ELSE 'Fraco (0-49)' END AS faixa,
  COUNT(*) AS total
FROM ${PREFIX}postmeta WHERE meta_key = 'rank_math_seo_score' GROUP BY faixa;" --allow-root --path=$PATH

# Top 404s (ultimos 30 dias)
wp db query "SELECT uri, SUM(times_accessed) as total FROM ${PREFIX}rank_math_404_logs
GROUP BY uri ORDER BY total DESC LIMIT 20;" --allow-root --path=$PATH

# Tamanho options (detectar bloat WooCommerce)
wp db query "SELECT option_name, LENGTH(option_value) as bytes FROM ${PREFIX}options
WHERE option_name LIKE 'rank%' AND autoload='yes' ORDER BY bytes DESC;" --allow-root --path=$PATH

# Listar TODAS as meta keys Rank Math existentes
wp db query "SELECT DISTINCT meta_key FROM ${PREFIX}postmeta WHERE meta_key LIKE 'rank_math%' ORDER BY meta_key;" --allow-root --path=$PATH
```

### Passo 2 — estado modulos

```bash
wp option pluck rank_math_modules --allow-root --path=$PATH
wp option pluck rank-math-options-general strip_category_base --allow-root --path=$PATH
wp option pluck rank-math-options-titles title_separator --allow-root --path=$PATH

# Ver tabelas custom (usa % wildcard — funciona com qualquer prefixo)
wp db query "SHOW TABLES LIKE '%rank_math%';" --allow-root --path=$PATH
```

---

## 2. Actualizar meta SEO (/rank-math update-meta)

### Meta key individual

```bash
# Titulo SEO
wp post meta update ID rank_math_title "%title% %sep% %sitename%" --allow-root --path=$PATH

# Descricao SEO (<160 chars)
wp post meta update ID rank_math_description "Texto conciso da pagina." --allow-root --path=$PATH

# Focus keyword
wp post meta update ID rank_math_focus_keyword "keyword1,keyword2" --allow-root --path=$PATH

# Robots (ARRAY JSON — obrigatorio --format=json)
wp post meta update ID rank_math_robots '["noindex"]' --format=json --allow-root --path=$PATH

# Advanced robots
wp post meta update ID rank_math_advanced_robots '{"max-snippet":"-1","max-video-preview":"-1","max-image-preview":"large"}' --format=json --allow-root --path=$PATH

# Canonical URL
wp post meta update ID rank_math_canonical_url "https://site.pt/url/" --allow-root --path=$PATH

# Pillar content
wp post meta update ID rank_math_pillar_content "on" --allow-root --path=$PATH

# Categoria primaria
wp post meta update ID rank_math_primary_category "15" --allow-root --path=$PATH

# Breadcrumb title
wp post meta update ID rank_math_breadcrumb_title "Titulo Breadcrumb" --allow-root --path=$PATH

# OpenGraph
wp post meta update ID rank_math_facebook_title "Titulo OG" --allow-root --path=$PATH
wp post meta update ID rank_math_facebook_description "Desc OG" --allow-root --path=$PATH
wp post meta update ID rank_math_facebook_image "https://site.pt/og.jpg" --allow-root --path=$PATH
wp post meta update ID rank_math_twitter_card_type "summary_large_image" --allow-root --path=$PATH

# Redirect per-post
wp post meta update ID rank_math_redirect_url_to "/nova-url/" --allow-root --path=$PATH
wp post meta update ID rank_math_redirection_header_code "301" --allow-root --path=$PATH
```

### Bulk update (wp eval)

```bash
# Aplicar titulo padrao a todos os posts sem titulo
wp eval '
$posts = get_posts(["numberposts"=>-1,"post_status"=>"publish"]);
$updated = 0;
foreach ($posts as $p) {
    if (!get_post_meta($p->ID, "rank_math_title", true)) {
        update_post_meta($p->ID, "rank_math_title", "%title% %sep% %sitename%");
        $updated++;
    }
}
echo "$updated posts actualizados\n";
' --allow-root --path=$PATH

# Aplicar descricao a partir do excerpt
wp eval '
$posts = get_posts(["post_type"=>"post","numberposts"=>-1,"post_status"=>"publish"]);
$updated = 0;
foreach ($posts as $p) {
    if (!get_post_meta($p->ID, "rank_math_description", true) && $p->post_excerpt) {
        $desc = wp_trim_words($p->post_excerpt, 20, "...");
        update_post_meta($p->ID, "rank_math_description", $desc);
        $updated++;
    }
}
echo "$updated posts actualizados\n";
' --allow-root --path=$PATH
```

### Bulk via shell

```bash
# Actualizar titulo de todos os posts publicados
wp post list --post_type=post --post_status=publish --field=ID --allow-root --path=$PATH | while read id; do
  wp post meta update $id rank_math_title "%title% %sep% %sitename%" --allow-root --path=$PATH
done

# Noindex em todos os drafts
wp post list --post_type=post --post_status=draft --field=ID --allow-root --path=$PATH | while read id; do
  wp post meta update $id rank_math_robots '["noindex"]' --format=json --allow-root --path=$PATH
done

# Paralelo com xargs
wp post list --post_type=page --post_status=publish --field=ID --allow-root --path=$PATH | \
  xargs -P4 -I {} wp post meta delete {} rank_math_canonical_url --allow-root --path=$PATH
```

### Bulk via CSV

```bash
# Formato CSV: id,descricao
while IFS=, read -r post_id desc; do
  wp post meta update "$post_id" rank_math_description "$desc" --allow-root --path=$PATH
done < descriptions.csv
```

---

## 3. Options globais (/rank-math options)

**Regra critica:** SEMPRE usar `wp option patch`, NUNCA `wp option update` em options serializadas.

```bash
# Separador de titulo
wp option patch update rank-math-options-titles title_separator "-" --allow-root --path=$PATH

# Templates por post type
wp option patch update rank-math-options-titles pt_post_title "%title% %sep% %sitename%" --allow-root --path=$PATH
wp option patch update rank-math-options-titles pt_page_title "%title% %sep% %sitename%" --allow-root --path=$PATH
wp option patch update rank-math-options-titles pt_product_title "%title% %sep% %currentyear% %sep% %sitename%" --allow-root --path=$PATH

# Homepage
wp option patch update rank-math-options-titles homepage_title "Titulo Site" --allow-root --path=$PATH
wp option patch update rank-math-options-titles homepage_description "Descricao Site" --allow-root --path=$PATH

# Remover base de categoria do URL
wp option patch update rank-math-options-general strip_category_base "on" --allow-root --path=$PATH
wp rewrite flush --allow-root --path=$PATH  # OBRIGATORIO apos esta alteracao

# Breadcrumbs
wp option patch update rank-math-options-general breadcrumbs_separator ">" --allow-root --path=$PATH

# Nofollow links externos
wp option patch update rank-math-options-general nofollow_external_links "on" --allow-root --path=$PATH

# Knowledge Graph
wp option patch update rank-math-options-titles knowledgegraph_type "Organization" --allow-root --path=$PATH
wp option patch update rank-math-options-titles knowledgegraph_name "Descomplicar" --allow-root --path=$PATH

# Webmaster verification
wp option patch update rank-math-options-general google_verify "codigo" --allow-root --path=$PATH

# Verificar sub-key
wp option pluck rank-math-options-titles title_separator --allow-root --path=$PATH

# LER option completa (diagnostico)
wp option get rank-math-options-general --format=json --allow-root --path=$PATH
```

### Gestao de modulos

```bash
# LER modulos activos
wp option get rank_math_modules --format=json --allow-root --path=$PATH

# ACTIVAR modulo (via wp eval — dispara hooks)
wp eval '
$modules = get_option("rank_math_modules", []);
if (!in_array("redirections", $modules)) {
    $modules[] = "redirections";
    update_option("rank_math_modules", $modules);
    echo "Modulo activado.\n";
} else { echo "Ja activo.\n"; }
' --allow-root --path=$PATH

# DESACTIVAR modulo
wp eval '
$modules = get_option("rank_math_modules", []);
$modules = array_values(array_diff($modules, ["404-monitor"]));
update_option("rank_math_modules", $modules);
echo "Modulo desactivado.\n";
' --allow-root --path=$PATH

# Modulos disponiveis:
# sitemap, rich-snippet, redirections, 404-monitor, local-seo,
# woocommerce, image-seo, link-counter, instant-indexing, content-ai,
# analytics, role-manager, acf, amp, news-sitemap (PRO), video-sitemap (PRO)
```

---

## 4. Schema markup (/rank-math schema)

Schema armazenado como array PHP em `rank_math_schema_{Type}`. Usar `wp eval` (nao JSON directo).

### Estrutura interna

```json
{
  "metadata": {"title": "Article", "type": "custom", "shortcode": "s-UNIQUE", "isPrimary": "1"},
  "@type": "Article",
  "headline": "%seo_title%",
  "description": "%seo_description%"
}
```

- `type: "template"` — herda globals | `type: "custom"` — personalizado
- `isPrimary: "1"` — schema principal do post
- Free: 1 schema/post | PRO: ilimitados

### Criar schemas

```bash
# Article
wp eval '
update_post_meta(ID, "rank_math_schema_Article", [
    "metadata" => ["title"=>"Article","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"1"],
    "@type"=>"Article",
    "headline"=>"%seo_title%",
    "description"=>"%seo_description%",
    "keywords"=>"%keywords%",
    "articleSection"=>"%categories%",
    "image"=>["@type"=>"ImageObject","url"=>"%post_thumbnail%"],
    "author"=>["@type"=>"Person","name"=>"%name%"],
    "publisher"=>["@type"=>"Organization","name"=>"%sitename%","logo"=>""],
    "datePublished"=>"%date(Y-m-dTH:i:sP)%",
    "dateModified"=>"%modified(Y-m-dTH:i:sP)%"
]);
echo "Schema Article criado\n";
' --user=1 --allow-root --path=$PATH

# FAQ
wp eval '
update_post_meta(ID, "rank_math_schema_FAQPage", [
    "metadata" => ["title"=>"FAQ","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"0"],
    "@type"=>"FAQPage",
    "mainEntity"=>[
        ["@type"=>"Question","name"=>"Pergunta 1","acceptedAnswer"=>["@type"=>"Answer","text"=>"Resposta 1"]],
        ["@type"=>"Question","name"=>"Pergunta 2","acceptedAnswer"=>["@type"=>"Answer","text"=>"Resposta 2"]]
    ]
]);
echo "Schema FAQ criado\n";
' --user=1 --allow-root --path=$PATH

# Product (WooCommerce)
wp eval '
update_post_meta(ID, "rank_math_schema_Product", [
    "metadata" => ["title"=>"Product","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"1"],
    "@type"=>"Product",
    "name"=>"%title%",
    "description"=>"%excerpt%",
    "sku"=>"SKU-001",
    "brand"=>["@type"=>"Brand","name"=>"Marca"],
    "image"=>["@type"=>"ImageObject","url"=>"%post_thumbnail%"],
    "offers"=>[
        "@type"=>"Offer","price"=>"29.99","priceCurrency"=>"EUR",
        "availability"=>"https://schema.org/InStock","url"=>"%url%",
        "priceValidUntil"=>"2026-12-31"
    ]
]);
echo "Schema Product criado\n";
' --user=1 --allow-root --path=$PATH

# LocalBusiness
wp eval '
update_post_meta(ID, "rank_math_schema_LocalBusiness", [
    "metadata" => ["title"=>"LocalBusiness","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"1"],
    "@type"=>"LocalBusiness",
    "name"=>"Nome Empresa",
    "description"=>"%seo_description%",
    "url"=>"%url%",
    "email"=>"email@empresa.pt",
    "telephone"=>"+351 XXX XXX XXX",
    "address"=>["@type"=>"PostalAddress","streetAddress"=>"Rua X","addressLocality"=>"Cidade","postalCode"=>"XXXX-XXX","addressCountry"=>"PT"],
    "geo"=>["@type"=>"GeoCoordinates","latitude"=>"38.7","longitude"=>"-9.1"],
    "openingHoursSpecification"=>[
        ["@type"=>"OpeningHoursSpecification","dayOfWeek"=>["Monday","Tuesday","Wednesday","Thursday","Friday"],"opens"=>"09:00","closes"=>"18:00"]
    ],
    "image"=>"%post_thumbnail%"
]);
echo "Schema LocalBusiness criado\n";
' --user=1 --allow-root --path=$PATH

# VideoObject
wp eval '
update_post_meta(ID, "rank_math_schema_VideoObject", [
    "metadata" => ["title"=>"VideoObject","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"0"],
    "@type"=>"VideoObject",
    "name"=>"%seo_title%",
    "description"=>"%seo_description%",
    "thumbnailUrl"=>"%post_thumbnail%",
    "uploadDate"=>"%date(Y-m-dTH:i:sP)%",
    "contentUrl"=>"https://exemplo.pt/video.mp4",
    "embedUrl"=>"https://youtube.com/embed/VIDEO_ID"
]);
echo "Schema VideoObject criado\n";
' --user=1 --allow-root --path=$PATH
```

### Actualizar/apagar schema

```bash
# Actualizar campo especifico
wp eval '
$schema = get_post_meta(ID, "rank_math_schema_Article", true);
if ($schema) {
    $schema["headline"] = "Novo Titulo";
    update_post_meta(ID, "rank_math_schema_Article", $schema);
    echo "Actualizado.\n";
}
' --allow-root --path=$PATH

# Apagar schema corrompido
wp post meta delete ID rank_math_schema_Article --allow-root --path=$PATH

# Apagar TODOS os schemas de um post
wp eval '
global $wpdb;
$wpdb->query($wpdb->prepare(
    "DELETE FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key LIKE %s",
    ID, "rank_math_schema_%"
));
echo "Todos os schemas eliminados.\n";
' --allow-root --path=$PATH
```

---

## 5. Redirects (/rank-math redirects)

### CRUD via API PHP (recomendado)

```bash
# Criar redirect 301
wp eval '
\RankMath\Redirections\DB::add([
    "id"=>"",
    "sources"=>[["pattern"=>"/url-antiga/","comparison"=>"exact"]],
    "url_to"=>"/url-nova/",
    "header_code"=>301,
    "status"=>"active",
]);
echo "Redirect criado\n";
' --allow-root --path=$PATH

# Redirect regex
wp eval '
\RankMath\Redirections\DB::add([
    "id"=>"",
    "sources"=>[["pattern"=>"^/blog/2023/.*$","comparison"=>"regex"]],
    "url_to"=>"/blog/",
    "header_code"=>301,
    "status"=>"active",
]);
' --allow-root --path=$PATH

# Redirect 410 (conteudo removido)
wp eval '
\RankMath\Redirections\DB::add([
    "id"=>"",
    "sources"=>[["pattern"=>"/removida/","comparison"=>"exact"]],
    "url_to"=>"",
    "header_code"=>410,
    "status"=>"active",
]);
' --allow-root --path=$PATH

# Actualizar
wp eval '
\RankMath\Redirections\DB::update([
    "id"=>42,
    "sources"=>[["pattern"=>"/url-antiga/","comparison"=>"exact"]],
    "url_to"=>"/url-nova/",
    "header_code"=>301,
    "status"=>"active",
]);
echo "Actualizado\n";
' --allow-root --path=$PATH

# Apagar
wp eval '\RankMath\Redirections\DB::delete(42); echo "Eliminado\n";' --allow-root --path=$PATH
wp eval '\RankMath\Redirections\DB::delete([42, 43, 44]); echo "Eliminados\n";' --allow-root --path=$PATH

# Bulk criar
wp eval '
$redirects = [
    ["/antiga-1/", "/nova-1/", 301],
    ["/antiga-2/", "/nova-2/", 301],
    ["/removida/", "", 410],
];
foreach ($redirects as $r) {
    $id = \RankMath\Redirections\DB::add([
        "id"=>"", "sources"=>[["pattern"=>$r[0],"comparison"=>"exact"]],
        "url_to"=>$r[1], "header_code"=>$r[2], "status"=>"active",
    ]);
    echo "#{$id}: {$r[0]} -> {$r[1]} ({$r[2]})\n";
}
' --allow-root --path=$PATH

# Validar
curl -I -s https://site.pt/url-antiga/ | grep HTTP
```

### Tipos de comparacao

| comparison | Comportamento |
|-----------|---------------|
| `exact` | Match exacto |
| `contains` | URL contem pattern |
| `start` | URL comeca com pattern |
| `end` | URL termina com pattern |
| `regex` | Expressao regular |

### 404 Monitor

```bash
# Top 404s
wp db query "SELECT uri, SUM(times_accessed) as total FROM ${PREFIX}rank_math_404_logs
GROUP BY uri ORDER BY total DESC LIMIT 20;" --allow-root --path=$PATH

# Limpar logs antigos (>30 dias)
wp db query "DELETE FROM ${PREFIX}rank_math_404_logs WHERE accessed < DATE_SUB(NOW(), INTERVAL 30 DAY);" --allow-root --path=$PATH

# Truncar tudo
wp db query "TRUNCATE TABLE ${PREFIX}rank_math_404_logs;" --allow-root --path=$PATH
```

---

## 6. Sitemap (/rank-math sitemap)

```bash
# Regenerar sitemap
wp rankmath sitemap generate --allow-root --path=$PATH

# Invalidar cache de sitemap via API PHP
wp eval '\RankMath\Sitemap\Cache::invalidate_storage(); echo "Cache invalidada.\n";' --allow-root --path=$PATH

# Invalidar sitemap de tipo especifico
wp eval '\RankMath\Sitemap\Cache::invalidate_storage("post"); echo "Sitemap posts invalidado.\n";' --allow-root --path=$PATH

# Incluir/excluir post-types
wp option patch update rank-math-options-sitemap pt_post_sitemap "on" --allow-root --path=$PATH
wp option patch update rank-math-options-sitemap pt_attachment_sitemap "off" --allow-root --path=$PATH

# Excluir posts especificos
wp eval '
$opts = get_option("rank-math-options-sitemap");
$opts["exclude_posts"] = "123,456,789";
update_option("rank-math-options-sitemap", $opts);
' --allow-root --path=$PATH

# Itens por pagina (reduzir em sites grandes)
wp option patch update rank-math-options-sitemap items_per_page "200" --allow-root --path=$PATH

# Verificar setting
wp option pluck rank-math-options-sitemap pt_post_sitemap --allow-root --path=$PATH
```

**Nginx rewrite rules (obrigatorio em Nginx puro):**
```nginx
rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
rewrite ^/([a-z]+)?-sitemap\.xsl$ /index.php?xsl=$1 last;
```

---

## 7. Instant Indexing (/rank-math indexing) — PRO

### IndexNow (integrado no Rank Math)

```bash
# Submeter URL individual
wp eval '
$url = "https://site.pt/novo-artigo/";
$api_key = get_option("rank-math-options-instant-indexing")["indexnow_api_key"] ?? "";
$response = wp_remote_post("https://api.indexnow.org/indexnow", [
    "headers" => ["Content-Type" => "application/json"],
    "body" => json_encode([
        "host" => parse_url(home_url(), PHP_URL_HOST),
        "key" => $api_key,
        "urlList" => [$url],
    ]),
]);
echo "Codigo: " . wp_remote_retrieve_response_code($response) . "\n";
' --allow-root --path=$PATH

# Submeter URLs em bulk
wp eval '
$urls = ["https://site.pt/post-1/", "https://site.pt/post-2/", "https://site.pt/post-3/"];
$api_key = get_option("rank-math-options-instant-indexing")["indexnow_api_key"] ?? "";
$response = wp_remote_post("https://api.indexnow.org/indexnow", [
    "headers" => ["Content-Type" => "application/json"],
    "body" => json_encode([
        "host" => parse_url(home_url(), PHP_URL_HOST),
        "key" => $api_key,
        "urlList" => $urls,
    ]),
]);
echo "Codigo: " . wp_remote_retrieve_response_code($response) . "\n";
' --allow-root --path=$PATH

# Submeter todos os posts publicados nos ultimos 7 dias
wp eval '
$posts = get_posts(["numberposts"=>-1,"post_status"=>"publish","date_query"=>[["after"=>"7 days ago"]]]);
$urls = array_map(function($p) { return get_permalink($p->ID); }, $posts);
$api_key = get_option("rank-math-options-instant-indexing")["indexnow_api_key"] ?? "";
$chunks = array_chunk($urls, 10000); // IndexNow max 10k por pedido
foreach ($chunks as $chunk) {
    $response = wp_remote_post("https://api.indexnow.org/indexnow", [
        "headers" => ["Content-Type" => "application/json"],
        "body" => json_encode([
            "host" => parse_url(home_url(), PHP_URL_HOST),
            "key" => $api_key,
            "urlList" => $chunk,
        ]),
    ]);
    echo count($chunk) . " URLs submetidos. Codigo: " . wp_remote_retrieve_response_code($response) . "\n";
}
' --allow-root --path=$PATH

# Verificar API key
wp eval 'echo get_option("rank-math-options-instant-indexing")["indexnow_api_key"] ?? "nao definida"; echo "\n";' --allow-root --path=$PATH

# Configurar post types para IndexNow
wp option patch update rank-math-options-instant-indexing bing_post_types '["post","page","product"]' --format=json --allow-root --path=$PATH
```

**Codigos resposta IndexNow:** 200=sucesso, 202=aceite (pendente), 400=invalido, 403=key invalida, 429=rate limit.

### Google Indexing API (via classes RM PRO)

```bash
# Submeter URL via API interna RM
wp eval '
$target = get_permalink(99);
\RankMath\Instant_Indexing\Api::submit($target, "URL_UPDATED");
echo "Submetido: $target\n";
' --allow-root --path=$PATH
```

**Limites:** Google Indexing API quota ~200 pedidos/dia. Nao submeter URLs voláteis (parametrizados, facturacao).

---

## 8. Analytics PRO (/rank-math analytics)

### Tabelas de analytics (apenas com modulo analytics activo + PRO)

```bash
# Dados Google Search Console armazenados
wp db query "SELECT page, SUM(clicks) as clicks, SUM(impressions) as impressions,
AVG(position) as avg_pos, AVG(ctr) as avg_ctr
FROM ${PREFIX}rank_math_analytics_gsc
WHERE created >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY page ORDER BY clicks DESC LIMIT 20;" --allow-root --path=$PATH

# Top queries
wp db query "SELECT query, SUM(clicks) as clicks, SUM(impressions) as impressions,
AVG(position) as avg_pos
FROM ${PREFIX}rank_math_analytics_gsc
WHERE created >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY query ORDER BY clicks DESC LIMIT 30;" --allow-root --path=$PATH

# Objectos rastreados (seo score, indexabilidade)
wp db query "SELECT page, seo_score, is_indexable, object_type
FROM ${PREFIX}rank_math_analytics_objects
ORDER BY seo_score ASC LIMIT 20;" --allow-root --path=$PATH

# Posts com pior ranking
wp db query "SELECT gsc.page, gsc.query, AVG(gsc.position) as avg_pos, ao.seo_score
FROM ${PREFIX}rank_math_analytics_gsc gsc
LEFT JOIN ${PREFIX}rank_math_analytics_objects ao ON gsc.page = ao.page
WHERE gsc.created >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY gsc.page, gsc.query
HAVING avg_pos > 10
ORDER BY gsc.impressions DESC LIMIT 20;" --allow-root --path=$PATH

# Google Analytics pageviews (se integrado)
wp db query "SELECT page, SUM(pageviews) as views, SUM(visitors) as visitors
FROM ${PREFIX}rank_math_analytics_ga
GROUP BY page ORDER BY views DESC LIMIT 20;" --allow-root --path=$PATH

# Links internos
wp db query "SELECT post_id, internal_link_count, external_link_count, incoming_link_count
FROM ${PREFIX}rank_math_internal_meta
ORDER BY incoming_link_count ASC LIMIT 20;" --allow-root --path=$PATH
```

---

## 9. Taxonomias e WooCommerce (/rank-math taxonomy)

### Terms (categorias, tags, taxonomias custom)

```bash
# LER titulo SEO de categoria
wp term meta get 15 rank_math_title --allow-root --path=$PATH

# DEFINIR description SEO
wp term meta update 23 rank_math_description "Descricao SEO da tag." --allow-root --path=$PATH

# DEFINIR noindex numa taxonomia
wp term meta update 23 rank_math_robots '["noindex"]' --format=json --allow-root --path=$PATH
# CUIDADO: noindex numa categoria remove do indice TODAS as paginas arquivo dessa cat
```

### WooCommerce bulk

```bash
# Titulo SEO para todas as categorias de produto sem titulo
wp eval '
$terms = get_terms(["taxonomy"=>"product_cat","hide_empty"=>false]);
$updated = 0;
foreach ($terms as $t) {
    if (!get_term_meta($t->term_id, "rank_math_title", true)) {
        update_term_meta($t->term_id, "rank_math_title", "%term% %sep% %sitename%");
        $updated++;
    }
}
echo "$updated categorias actualizadas\n";
' --allow-root --path=$PATH

# Descricao SEO para categorias de produto a partir da descricao da categoria
wp eval '
$terms = get_terms(["taxonomy"=>"product_cat","hide_empty"=>false]);
$updated = 0;
foreach ($terms as $t) {
    if (!get_term_meta($t->term_id, "rank_math_description", true) && $t->description) {
        $desc = wp_trim_words($t->description, 25, "...");
        update_term_meta($t->term_id, "rank_math_description", $desc);
        $updated++;
    }
}
echo "$updated categorias actualizadas\n";
' --allow-root --path=$PATH

# Noindex em taxonomias de atributos vazias (cor, tamanho, etc.)
wp eval '
$taxonomies = wc_get_attribute_taxonomy_names();
$noindexed = 0;
foreach ($taxonomies as $tax) {
    $terms = get_terms(["taxonomy"=>$tax,"hide_empty"=>true]);
    foreach ($terms as $t) {
        if (!get_term_meta($t->term_id, "rank_math_robots", true)) {
            update_term_meta($t->term_id, "rank_math_robots", ["noindex"]);
            $noindexed++;
        }
    }
}
echo "$noindexed atributos noindexados\n";
' --allow-root --path=$PATH
```

### User meta (arquivo de autor)

```bash
wp user meta update 1 rank_math_title "Nome Autor — Especialista WordPress" --allow-root --path=$PATH
wp user meta update 1 rank_math_description "Artigos sobre WordPress e SEO." --allow-root --path=$PATH
```

---

## 10. News Sitemap PRO (/rank-math news)

```bash
# Activar modulo
wp eval '
$modules = get_option("rank_math_modules", []);
if (!in_array("news-sitemap", $modules)) { $modules[] = "news-sitemap"; update_option("rank_math_modules", $modules); }
echo "News Sitemap activo\n";
' --allow-root --path=$PATH

# Definir meta por post
wp post meta update ID rank_math_news_sitemap_robots "index" --allow-root --path=$PATH
wp post meta update ID rank_math_news_sitemap_genres "PressRelease" --allow-root --path=$PATH
# Generos: Blog, PressRelease, Satire, OpEd
wp post meta update ID rank_math_news_sitemap_keywords "ia,automacao,marketing" --allow-root --path=$PATH
wp post meta update ID rank_math_news_sitemap_stock_tickers "GOOG,MSFT" --allow-root --path=$PATH

# Configurar post types para news sitemap
wp eval '
$opts = get_option("rank-math-options-sitemap");
$opts["news_sitemap_post_type"] = ["post"];
update_option("rank-math-options-sitemap", $opts);
' --allow-root --path=$PATH
```

---

## 11. Hooks e filtros (/rank-math hooks)

### Frontend

```bash
# Modificar titulo via filtro (persistente via rank-math.php)
wp eval '
file_put_contents(get_stylesheet_directory() . "/rank-math.php",
"<?php\n// Filtros custom Rank Math\n" .
"add_filter(\"rank_math/frontend/show_keywords\", \"__return_true\");\n"
);
echo "Filtro adicionado.\n";
' --allow-root --path=$PATH
```

### Hooks uteis (para rank-math.php ou mu-plugin)

```php
// rank_math/frontend/robots — modificar robots programaticamente
add_filter('rank_math/frontend/robots', function($robots) {
    if (is_tax('product_cat')) { $robots['noindex'] = 'noindex'; }
    return $robots;
});

// rank_math/json_ld — modificar todo o output JSON-LD
add_filter('rank_math/json_ld', function($data, $jsonld) { return $data; }, 10, 2);

// rank_math/snippet/rich_snippet_article_entity — modificar schema Article
add_filter('rank_math/snippet/rich_snippet_article_entity', function($entity) {
    $entity['headline'] = 'Titulo Custom';
    return $entity;
});

// rank_math/sitemap/enable_caching — desactivar cache sitemap
add_filter('rank_math/sitemap/enable_caching', '__return_false');

// rank_math/focus_keyword/maxtags — alterar limite keywords (Free: 5)
add_filter('rank_math/focus_keyword/maxtags', function() { return 10; });

// rank_math/metabox/priority — baixar prioridade do metabox
add_filter('rank_math/metabox/priority', function() { return 'low'; });

// rank_math/sitemap/remove_credit
add_filter('rank_math/sitemap/remove_credit', '__return_true');
```

**Ficheiro auto-incluido:** O Rank Math carrega automaticamente `/wp-content/themes/{tema-activo}/rank-math.php` quando o plugin esta activo. Colocar filtros persistentes aqui.

---

## 12. Backup e rollback (/rank-math backup)

### Backup antes de operacoes em massa

```bash
# Exportar options Rank Math
wp eval '
$options = ["rank-math-options-general","rank-math-options-titles","rank-math-options-sitemap","rank_math_modules","rank-math-options-instant-indexing"];
$backup = [];
foreach ($options as $opt) { $backup[$opt] = get_option($opt); }
file_put_contents("/tmp/rankmath-options-backup.json", json_encode($backup, JSON_PRETTY_PRINT));
echo "Options exportadas para /tmp/rankmath-options-backup.json\n";
' --allow-root --path=$PATH

# Exportar todos os post meta Rank Math
wp eval '
global $wpdb;
$results = $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE meta_key LIKE \"rank_math_%\"", ARRAY_A);
file_put_contents("/tmp/rankmath-postmeta-backup.json", json_encode($results));
echo count($results) . " meta entries exportadas\n";
' --allow-root --path=$PATH

# Exportar redirects
wp db query "SELECT * FROM ${PREFIX}rank_math_redirections;" --allow-root --path=$PATH > /tmp/rankmath-redirections-backup.tsv

# Exportar dados SEO para CSV
wp eval '
$posts = get_posts(["post_type"=>"post","post_status"=>"publish","numberposts"=>-1]);
$fp = fopen("/tmp/rankmath-seo-export.csv", "w");
fputcsv($fp, ["ID","Titulo","SEO_Title","Description","Focus_KW","Score","Canonical"]);
foreach ($posts as $p) {
    fputcsv($fp, [
        $p->ID, $p->post_title,
        get_post_meta($p->ID, "rank_math_title", true),
        get_post_meta($p->ID, "rank_math_description", true),
        get_post_meta($p->ID, "rank_math_focus_keyword", true),
        get_post_meta($p->ID, "rank_math_seo_score", true),
        get_post_meta($p->ID, "rank_math_canonical_url", true),
    ]);
}
fclose($fp);
echo count($posts) . " posts exportados\n";
' --allow-root --path=$PATH
```

### Rollback

```bash
# Restaurar options de backup
wp eval '
$backup = json_decode(file_get_contents("/tmp/rankmath-options-backup.json"), true);
foreach ($backup as $key => $value) {
    update_option($key, $value);
    echo "Restaurado: $key\n";
}
' --allow-root --path=$PATH

# Restaurar post meta de backup
wp eval '
$entries = json_decode(file_get_contents("/tmp/rankmath-postmeta-backup.json"), true);
$restored = 0;
foreach ($entries as $e) {
    update_post_meta($e["post_id"], $e["meta_key"], maybe_unserialize($e["meta_value"]));
    $restored++;
}
echo "$restored meta entries restauradas\n";
' --allow-root --path=$PATH
```

---

## 13. Provisioning (/rank-math provision)

Configuracao completa de um site novo.

```bash
# 1. Activar modulos essenciais
wp eval '
update_option("rank_math_modules", ["sitemap","rich-snippet","redirections","404-monitor","seo-analysis","link-counter","instant-indexing","image-seo"]);
echo "Modulos configurados\n";
' --allow-root --path=$PATH

# 2. Configuracoes globais
wp option patch update rank-math-options-titles title_separator "-" --allow-root --path=$PATH
wp option patch update rank-math-options-titles knowledgegraph_type "Organization" --allow-root --path=$PATH
wp option patch update rank-math-options-titles knowledgegraph_name "Nome Empresa" --allow-root --path=$PATH
wp option patch update rank-math-options-general strip_category_base "on" --allow-root --path=$PATH
wp option patch update rank-math-options-general nofollow_external_links "on" --allow-root --path=$PATH
wp option patch update rank-math-options-general attachment_redirect_urls "on" --allow-root --path=$PATH
wp option patch update rank-math-options-sitemap pt_attachment_sitemap "off" --allow-root --path=$PATH

# 3. Templates por post type
wp option patch update rank-math-options-titles pt_post_title "%title% %sep% %sitename%" --allow-root --path=$PATH
wp option patch update rank-math-options-titles pt_page_title "%title% %sep% %sitename%" --allow-root --path=$PATH
wp option patch update rank-math-options-titles pt_post_description "%excerpt%" --allow-root --path=$PATH

# 4. Flush e regenerar
wp rewrite flush --allow-root --path=$PATH
wp rankmath sitemap generate --allow-root --path=$PATH

echo "Provisioning concluido"
```

---

## 14. Validacao pos-aplicacao

### Verificar schema no HTML

```bash
# Verificar JSON-LD no frontend
curl -s "https://site.pt/post/" | grep -o '"@type":"[^"]*"' | sort -u

# Extrair JSON-LD completo
curl -s "https://site.pt/post/" | grep -oP '<script type="application/ld\+json">\K[^<]+'

# Verificar meta robots
curl -s "https://site.pt/post/" | grep -i 'meta name="robots"'

# Verificar OG tags
curl -s "https://site.pt/post/" | grep -i 'property="og:'

# Verificar canonical
curl -s "https://site.pt/post/" | grep -i 'rel="canonical"'
```

### Confirmar meta na BD

```bash
wp post meta list ID --format=table --allow-root --path=$PATH | grep rank_math
wp post meta get ID rank_math_robots --format=json --allow-root --path=$PATH
```

---

## Sequencia de cache (sempre apos alteracoes)

```bash
wp cache flush --allow-root --path=$PATH
wp transient delete --all --allow-root --path=$PATH
wp rewrite flush --allow-root --path=$PATH
wp rankmath sitemap generate --allow-root --path=$PATH
# Limpar page cache (adaptar ao plugin):
# wp wp-rocket clean --confirm     # WP Rocket
# wp litespeed-purge all           # LiteSpeed
```

> Alteracoes via `wp db query` nao invalidam Redis/Memcached. Sempre executar `wp cache flush`.

---

## Variaveis dinamicas

**Post:** `%title%` `%excerpt%` `%seo_title%` `%seo_description%` `%url%` `%post_thumbnail%` `%id%` `%focuskw%` `%keywords%`
**Data:** `%date(Y-m-d)%` `%modified(Y-m-d)%` `%currentyear%` `%currentmonth%`
**Taxonomia:** `%category%` `%categories%` `%tag%` `%tags%` `%term%` `%term_description%` `%customterm(taxonomy)%`
**Site:** `%sep%` `%sitename%` `%sitedesc%` `%org_name%` `%org_logo%` `%org_url%`
**Autor:** `%name%` `%userid%` `%user_description%`
**Avancado:** `%customfield(field-name)%` `%search_query%` `%page%` `%pagenumber%`
**WooCommerce:** `%wc_price%` `%wc_sku%` `%wc_shortdesc%` `%wc_brand%`
**PRO:** `%randomword(a|b|c)%` `%imagealt%` `%imagetitle%`

---

## Gotchas criticos

| Problema | Causa | Solucao |
|----------|-------|---------|
| Robots nao aplicam | String em vez de array | `'["noindex"]' --format=json` |
| Options corrompidas | `wp option update` em serializada | Sempre `wp option patch` |
| Alteracao nao visivel | Object cache (Redis) | `wp cache flush` |
| Schema quebra editor | JSON invalido em schema_* | `wp post meta delete ID rank_math_schema_*` |
| Sitemap 404 no Nginx | Faltam rewrite rules | Adicionar regras ao server block |
| SEO score nao actualiza | Calculado no Gutenberg | Apagar `rank_math_seo_score` para recalcular |
| Bloat WooCommerce | options-titles por atributo | Redis + reduzir atributos |
| Titulo ignorado | Cache de pagina | Limpar plugin de cache |
| Permissoes bloqueiam | Sem contexto user | `--user=1` em todos os wp eval |
| Double serialization | wp option update com JSON | Usar wp option patch ou wp eval |
| Serialization corrupta | Search-replace directo | Usar `wp search-replace` (serialization-aware) |
| wp option patch sem hooks | patch nao dispara hooks | `wp eval` com `update_option()` para side-effects |

---

## Capabilities Rank Math

`rank_math_general` `rank_math_sitemap` `rank_math_404_monitor` `rank_math_redirections` `rank_math_role_manager` `rank_math_analytics` `rank_math_onpage_general` `rank_math_onpage_advanced` `rank_math_onpage_snippet` `rank_math_onpage_social` `rank_math_content_ai`

---

## Free vs PRO — funcionalidades WP-CLI

| Funcionalidade | Free | PRO |
|---------------|------|-----|
| `wp rankmath sitemap generate` | sim | sim |
| Meta por post/term/user | sim | sim |
| Options globais | sim | sim |
| Schema markup (1/post) | sim | sim |
| Schemas multiplos/post | nao | sim |
| Custom Schema Builder (840+ tipos) | nao | sim |
| Redirections | sim | sim |
| 404 Monitor | sim | sim |
| Instant Indexing (IndexNow) | sim | sim |
| Google Indexing API | nao | sim |
| Analytics GSC/GA | basico | avancado |
| News Sitemap | nao | sim |
| Video Sitemap | nao | sim |
| Content AI | limitado | sim |
| CSV import/export SEO | nao | sim |
| Focus keywords ilimitadas | 5 max | ilimitado |

---

## Migracao Yoast -> Rank Math

```bash
# BACKUP OBRIGATORIO
wp db export backup-pre-migracao-$(date +%Y%m%d).sql --allow-root --path=$PATH

# Detectar prefixo
PREFIX=$(wp db prefix --allow-root --path=$PATH)

# Migrar titulo SEO
wp db query "INSERT INTO ${PREFIX}postmeta (post_id, meta_key, meta_value)
SELECT post_id, 'rank_math_title', meta_value FROM ${PREFIX}postmeta
WHERE meta_key = '_yoast_wpseo_title' AND meta_value != ''
AND post_id NOT IN (SELECT post_id FROM ${PREFIX}postmeta WHERE meta_key = 'rank_math_title');" --allow-root --path=$PATH

# Migrar meta description
wp db query "INSERT INTO ${PREFIX}postmeta (post_id, meta_key, meta_value)
SELECT post_id, 'rank_math_description', meta_value FROM ${PREFIX}postmeta
WHERE meta_key = '_yoast_wpseo_metadesc' AND meta_value != ''
AND post_id NOT IN (SELECT post_id FROM ${PREFIX}postmeta WHERE meta_key = 'rank_math_description');" --allow-root --path=$PATH

# Migrar focus keyword
wp db query "INSERT INTO ${PREFIX}postmeta (post_id, meta_key, meta_value)
SELECT post_id, 'rank_math_focus_keyword', meta_value FROM ${PREFIX}postmeta
WHERE meta_key = '_yoast_wpseo_focuskw' AND meta_value != ''
AND post_id NOT IN (SELECT post_id FROM ${PREFIX}postmeta WHERE meta_key = 'rank_math_focus_keyword');" --allow-root --path=$PATH

# Sempre seguido de:
wp cache flush --allow-root --path=$PATH
```

---

## Recursos

### Referencias

- **`references/commands.md`** — Referencia completa de meta keys, options e modulos

### Documentacao Hub

- **Manual:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI/Rank-Math-WP-CLI-Manual-Definitivo.md` (17 seccoes)
- **QR:** `Hub/06-Operacoes/Documentacao/Quick-Reference/QR-RankMath-WP-CLI.md`
- **Pesquisa Claude:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI/Rank-Math-SEO-WP-CLI-Skils-Pesquisa-Claude.md`
- **Pesquisa Gemini:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI/Rank-Math-SEO-WP-CLI-Skills-Pesquisa-Gemini.md`
- **NotebookLM:** [WordPress Config CLI](https://notebooklm.google.com/notebook/fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d) (4 fontes)

---

*Rank Math SEO via WP-CLI | Descomplicar | v2.0.0 | 29-03-2026*

---

## Healing Log

Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar.

```jsonl
{"date":"","issue":"","fix":"","source":"user|auto"}
```

*Adicionar nova linha após cada erro corrigido.*
