SEO & Conteúdo

SEO Técnico para Magento 2 e Adobe Commerce: Guia Completo

Como controlar indexação, canonicalização, dados estruturados, sitemap, hreflang e crawl budget em lojas Magento 2 / Adobe Commerce — não a teoria genérica do Google, mas o comportamento REAL do Magento: parâmetros nativos, schema da tabela url_rewrite, gotchas por versão, comandos CLI e o que o core gera vs o que exige módulo.

Por Roger Takemiya · Atualizado em 14 de junho de 2026 · 26 min de leitura

Toda loja Magento 2 nasce com um problema silencioso de SEO técnico: a navegação por camadas (faceted navigation) gera dezenas de milhares de combinações de URLs com parâmetros, o canonical nativo das páginas paginadas executa o anti-padrão que o próprio Google condena, e o core não emite hreflang nem JSON-LD completo sem módulo. O resultado é desperdício de crawl budget, canonicalização errada e perda de posições para quem domina o que o Magento faz de verdade por baixo do Admin.

A maioria dos guias de "SEO Magento" repete a teoria do Google e mostra dois ou três caminhos do Admin. Este aqui é diferente de propósito: cada seção carrega o parâmetro real (product_list_order, product_list_dir, product_list_limit, p), o schema da tabela (url_rewrite com redirect_type 0/301/302), o comando CLI exato, a versão onde o comportamento muda e o gotcha que só aparece em produção. Quando eu digo "o canonical do Magento canonicaliza ?p=2 para a categoria base", isso está ancorado em issue confirmada do repositório, não em achismo.

Em mais de 14 anos implementando Magento, aprendi que os ganhos mais rápidos não vêm de conteúdo, mas de corrigir indexação, duplicação e crawl budget — e que a diferença entre amador e especialista é saber que o Magento parcialmente contraria as boas práticas que ele mesmo documenta. É por aí que vamos. Performance é sinal de ranqueamento e tem guia próprio: Performance e Core Web Vitals no Magento 2.

Faceted navigation: os parâmetros REAIS do Magento e o gotcha robots vs noindex

A layered/faceted navigation é o maior gerador de URLs problemáticas em qualquer loja Magento. O erro número um dos guias genéricos é mostrar uma URL de filtro como ?color=blue&size=M. O Magento não gera isso. Quem constrói essas URLs é o módulo Magento_LayeredNavigation, e o formato real é parâmetro = attribute code do atributo e valor = ID da opção (não o texto do rótulo):

/bags.html?color=18          <- color é o attribute_code; 18 é o option_id de "Blue"
/bags.html?manufacturer=42   <- manufacturer code; 42 é o option_id
/bags.html?color=18&size=24  <- filtros combinados
/bags.html?price=100-200     <- atributos Price/Decimal entram como FAIXA, não ID

Esse detalhe é decisivo: como o parâmetro é o attribute code, o seu robots.txt precisa listar os codes do SEU catálogo, não nomes inventados. Se você instalou um módulo de SEO (Mirasvit/Amasty) que troca o parâmetro por um alias textual ou transforma o filtro em path (/bags/color/blue), as regras mudam de novo — audite a URL real antes de escrever qualquer Disallow.

Os parâmetros nativos de LISTAGEM que ninguém bloqueia (e deveria)

Além dos filtros, o Magento adiciona cinco parâmetros nativos de ordenação/paginação/exibição que multiplicam a mesma listagem em variações duplicadas. Esses são os que mais sangram crawl budget porque existem em toda categoria:

ParâmetroO que controlaValores reais
product_list_orderCritério de ordenaçãoattribute code de sorting: position, price, name
product_list_dirDireçãoasc | desc
product_list_limitItens por página12, 24, 36, all (conforme Catalog > Storefront)
product_list_modeLayout da listagemgrid | list
pNúmero da página2, 3, ... (paginação)

Combine isso mentalmente: uma categoria com 4 atributos filtráveis, 3 ordenações, 2 direções, 3 limites e 2 modos gera uma explosão combinatória que o Googlebot vai tentar rastrear. Por isso o crawl budget de catálogos Magento é gasto em lixo, não em produtos.

robots.txt específico para Magento (não o modelo genérico do Google)

Este é o bloco que eu aplico em loja Magento padrão (sem módulo que reescreva parâmetros). Ele bloqueia os parâmetros de listagem nativos e os filtros mais comuns — ajuste os codes de filtro ao seu catálogo:

User-agent: *
# parâmetros de listagem nativos do Magento
Disallow: /*?product_list_order=
Disallow: /*?product_list_dir=
Disallow: /*?product_list_limit=
Disallow: /*?product_list_mode=
Disallow: /*?*product_list_order=
Disallow: /*?*product_list_dir=
Disallow: /*?*product_list_limit=
Disallow: /*?*product_list_mode=
# filtros de faceted navigation (use os attribute_code do SEU catálogo)
Disallow: /*?*color=
Disallow: /*?*size=
Disallow: /*?*manufacturer=
Disallow: /*?*price=
# busca interna e sessão (vazam para o índice)
Disallow: /catalogsearch/
Disallow: /*?q=
Disallow: /*?SID=

Observação sobre paginação: eu deliberadamente NÃO coloco Disallow: /*?p= aqui. A paginação precisa permanecer rastreável para o Google descobrir produtos que só aparecem da página 2 em diante — isso é tratado em detalhe na seção de paginação.

O gotcha crítico: robots.txt e noindex são mutuamente exclusivos

Aqui está a falha conceitual que vejo em 9 de cada 10 auditorias — inclusive em versões anteriores deste guia. A receita popular de "três camadas" (robots.txt + noindex + canonical) é parcialmente contraditória, e por uma razão técnica simples: se você bloqueia a URL no robots.txt, o Googlebot nunca baixa a página, logo nunca lê o noindex nem o canonical. O próprio Google é explícito:

For the noindex rule to be effective, the page or resource must not be blocked by a robots.txt file, and it has to be otherwise accessible to the crawler. If the page is blocked by a robots.txt file or the crawler can't access the page, the crawler will never see the noindex rule, and the page can still appear in search results.

Fonte: Google Search Central

Pior: Disallow no robots.txt NÃO desindexa o que já foi indexado. Uma URL já no índice pode continuar aparecendo (sem snippet, com o aviso "nenhuma informação disponível") porque o Google a conhece de links externos. Então a decisão é binária, por URL:

  • URL nova, lixo puro (sort/dir/limit/mode): Disallow no robots.txt para nunca gastar crawl. Aceite que, se já indexou, vai precisar removê-la via GSC Removals — não via robots.
  • URL que você quer DESindexar: deixe rastreável (sem Disallow), sirva noindex, espere o Google recrawlear e cair do índice. Só DEPOIS de cair você pode bloquear no robots para economizar crawl.
  • Nunca combine Disallow + noindex na mesma URL ao mesmo tempo — o noindex vira letra morta.

E o core não ajuda

Detalhe que falta em todo guia genérico: o core do Magento NÃO adiciona noindex automático nas páginas de filtro. Não existe checkbox "noindex nas facetas" no Admin. Para isso você precisa de módulo de SEO (Mirasvit SEO Suite, Amasty SEO Toolkit) ou de customização via layout XML/observer que injete a meta robots condicionalmente quando há parâmetro de filtro na request. Sem isso, suas facetas ou estão indexáveis ou dependem só do robots.txt — e você já viu por que robots sozinho não desindexa.

Por fim, o ângulo de infraestrutura: filtros não bloqueados castigam o servidor com requisições caras e não cacheáveis (cada combinação é um cache MISS no Varnish), o que degrada TTFB e, por tabela, os Core Web Vitals das páginas que importam.

Paginação no Magento: rel=next/prev morto, canonical auto-referente e o anti-padrão nativo

Esta seção existe porque paginação no Magento concentra três erros ao mesmo tempo: orientação desatualizada, um anti-padrão embutido no core, e a tentação de usar noindex onde ele faz mais mal que bem. Vamos por partes.

1. rel=next / rel=prev foi descontinuado pelo Google

Em 21 de março de 2019 o Google anunciou que parou de usar rel="next" e rel="prev" como sinal de indexação. Implementar essas tags hoje não traz nenhum benefício de ranqueamento no Google (o Bing ainda as usa como hint). O Google passou a entender a sequência paginada pela estrutura de links internos rastreáveis. Conclusão prática: não gaste linha de template em rel=next/prev; gaste em garantir que os links de paginação sejam âncoras <a href> reais, não botões JavaScript que o crawler não segue.

2. O anti-padrão nativo do Magento: ?p=2 canonicaliza para a página base

Aqui está a contradição interna que os guias não resolvem. Quando você ativa o canonical de categorias, o Magento gera, em todas as páginas paginadas (?p=2, ?p=3...), um canonical apontando para a URL da categoria SEM o ?p. Ou seja, o core faz exatamente o anti-padrão "canonical de todas as páginas para a página 1" que o Google desaconselha. Isso está documentado em issue confirmada:

According to the documentation by Google: "Don't use the first page of a paginated sequence as the canonical page. Instead, give each page its own canonical URL."

Fonte: magento/magento2 — Issue #37338 (confirmada e reproduzida em 2.4.x)

O efeito colateral é grave: ao dizer "a página 2 é só uma cópia da página 1", você instrui o Google a ignorar os produtos que só existem da página 2 em diante. Em catálogos grandes, isso deixa metade do estoque órfão de indexação. O comportamento de tratamento de paginação no canonical variou entre as séries 2.3 e 2.4.x, mas até hoje (linha 2.4.x) o padrão do core continua canonicalizando para a base — confirme sempre na sua versão inspecionando o <head> de uma página ?p=2 em produção.

3. O que fazer: canonical auto-referente, não noindex

A orientação atual e correta é: cada página paginada deve canonicalizar para si mesma (?p=2 → canonical ?p=2) e os links de paginação devem ser rastreáveis. Eu evito noindex em paginação por um motivo concreto: noindex nas páginas 2+ acaba removendo do crawl, com o tempo, os produtos que só são linkados a partir delas (o Google trata noindex,follow como noindex,nofollow no longo prazo). Resumo da minha receita de produção:

  • Canonical auto-referente por página: sobrescreva o comportamento nativo (módulo de SEO ou plugin no bloco de canonical) para que ?p=N aponte para ?p=N.
  • Links rastreáveis: a paginação do tema Luma renderiza âncoras reais por padrão — confirme que não foi trocada por AJAX/infinite scroll sem fallback, senão os produtos das páginas internas viram órfãos.
  • Sem noindex em paginação: deixe as páginas indexáveis e auto-canônicas; quem decide o que mostrar é o Google.
  • View All como exceção: se você tem uma página "ver todos" (?product_list_limit=all) razoável em peso, ela pode ser a canônica da série — mas só se carregar rápido; em categoria com milhares de itens, all destrói o LCP e não vale.

Esse é o ponto onde o canonical nativo e a paginação se cruzam — e por que módulos de SEO existem: o controle granular que o core não oferece. Veja também a seção de canonical nativo.

Tags canonical nativas do Magento: o que ativar e onde o core erra

O Magento 2 / Adobe Commerce traz suporte nativo a canonical link meta tags, com os campos em Stores > Settings > Configuration > Catalog > Catalog > Search Engine Optimization. O default de fábrica é No para ambos os campos — ou seja, a instalação padrão sobe SEM canonical, e essa é a primeira coisa que eu corrijo em loja nova.

CampoDefaultQuando "Yes"Exemplo gerado
Use Canonical Link Meta Tag for CategoriesNoCanonical para o caminho completo da categoriamystore.com/gear/bags/
Use Canonical Link Meta Tag for ProductsNoCanonical no formato domínio + url-keymystore.com/driven-backpack.html

As a best practice, it is recommended that you enable canonical meta tags for both categories and products.

Fonte: Magento merchdocs (GitHub)

Por que isso importa: o produto em múltiplos caminhos

Por padrão, um mesmo produto pode ser acessado por vários caminhos de categoria (/gear/bags/driven-backpack.html e /sale/driven-backpack.html) além da URL curta. Sem o canonical de produtos ligado, o Google enxerga várias URLs concorrentes. Ativando Use Canonical Link Meta Tag for Products, todas apontam para domínio/url-key.html, consolidando os sinais.

O que o canonical nativo faz BEM — e o que ele NÃO faz

  • Bom — não inclui parâmetros de filtro: o canonical da categoria aponta para a URL limpa, sem os ?color=/?product_list_order=. Isso é o comportamento desejado para facetas.
  • Ruim — paginação: como detalhei na seção de paginação, o canonical de ?p=2 aponta para a categoria base (anti-padrão). O core contradiz a boa prática que ele próprio documenta.
  • Ausente — controle granular: não há como, no Admin, definir canonical por regra (ex.: "facetas de cor canonicalizam para a categoria, mas a faceta de marca tem canonical próprio"). Esse é exatamente o vácuo que módulos de SEO (Mirasvit, Amasty) preenchem.

Minha recomendação prática

Eu ativo ambos como Yes em quase todo projeto, e em seguida verifico/corrijo a paginação para canonical auto-referente. Lembre que o rel=canonical é um sinal forte, mas é um hint, não garantia: o Google pode escolher outra URL se houver sinais conflitantes (ver conteúdo duplicado). Por isso a regra de ouro continua: combinar canonical com 301 e sitemap coerente.

url_rewrite: schema da tabela, SQL de auditoria, 301 vs 302 e o bloat de URLs

A arquitetura de URLs do Magento vive na tabela url_rewrite. Entender o schema dela é o que separa quem "mexe no Admin" de quem audita SEO técnico de verdade. Estas são as colunas que importam:

ColunaO que guarda
url_rewrite_idPK auto-incremento
entity_typeproduct | category | cms-page | custom
entity_idID da entidade (product_id, category_id, page_id...)
request_pathURL solicitada (ex.: driven-backpack.html)
target_pathDestino interno ou URL final (ex.: catalog/product/view/id/42)
redirect_type0 = sem redirect (rewrite interno) · 301 = permanente · 302 = temporário
store_idEscopo de store view (rewrites diferentes por idioma)
is_autogenerated1 = gerado pelo sistema · 0 = override manual (Marketing)
metadataMetadados (ex.: categoria de origem do rewrite de produto)

301 vs 302: o que o Magento usa e quando

A distinção que a maioria ignora: redirect_type=0 é o caso mais comum — é o rewrite interno (a URL bonita que mapeia para a rota do controller, sem redirecionar o navegador). O Magento só grava 301 quando você muda uma URL key com redirect permanente ativo, e 302 em cenários temporários/manuais. Para SEO isso é crucial: 302 NÃO consolida sinal de canonicalização da mesma forma que 301. Em mudança permanente de catálogo, garanta sempre 301 — um 302 deixado por engano mantém a URL antiga competindo no índice.

SQL de auditoria que eu rodo em toda loja madura

A tabela facilmente passa de centenas de milhares de linhas. Estas queries encontram os dois problemas que mais doem — duplicatas e cadeias de redirect (A→B→C):

-- 1) request_paths duplicados (mesmo path apontando para destinos diferentes)
SELECT request_path, COUNT(*) AS qtd
FROM url_rewrite
GROUP BY request_path, store_id
HAVING COUNT(*) > 1
ORDER BY qtd DESC;

-- 2) cadeias de redirect: o target de uma linha é o request de outra
SELECT a.request_path AS origem, a.target_path AS intermediario, b.target_path AS destino_final
FROM url_rewrite a
JOIN url_rewrite b ON a.target_path = b.request_path
WHERE a.redirect_type IN (301,302) AND b.redirect_type IN (301,302);

-- 3) volume por tipo de redirect (saúde geral)
SELECT redirect_type, entity_type, COUNT(*)
FROM url_rewrite GROUP BY redirect_type, entity_type;

Cadeias de redirect desperdiçam crawl budget e diluem PageRank a cada salto — reescreva-as para apontar direto ao destino final.

O bloat: "Use Categories Path for Product URLs"

Eis a config que mais infla a tabela, na mesma seção de SEO de Catálogo: Use Categories Path for Product URLs. Quando ligada, o Magento gera um rewrite do produto para CADA categoria a que ele pertence. Um produto em N categorias = N linhas em url_rewrite. Em catálogo com produtos multi-categoria, isso multiplica a tabela e cada save de categoria regenera tudo em tempo real — fonte clássica de lentidão. O trade-off: URLs com path de categoria são "mais bonitas", mas criam mais variações duplicadas (que o canonical de produto precisa consolidar). Na maioria dos projetos eu deixo essa config como No e uso a URL curta do produto.

Sufixo .html e regeneração via CLI

Os campos Product URL Suffix e Category URL Suffix (tipicamente .html) devem ser decididos antes do lançamento. Mudar o sufixo depois força a recriação de todos os rewrites e uma onda de 301 que polui o índice por semanas. Para regenerar os rewrites de catálogo:

bin/magento indexer:reindex catalog_url_rewrite
bin/magento cache:flush

Um detalhe que pega muita gente: alterar catalog/seo/category_url_suffix, product_url_suffix ou product_use_categories não regenera automaticamente os rewrites — só uma atualização de url key da categoria ou o reindex acima dispara a recriação. Sempre confirme com a query de duplicatas depois de qualquer mudança dessas.

Gestão no Admin e migração

O Admin expõe tudo isso em Marketing > SEO & Search > URL Rewrites, com os quatro tipos (Product, Category, CMS Page, Custom). Use Custom para mapear URLs legadas em uma troca de plataforma — assunto tratado em detalhe no guia de Migração de Magento 1 para Magento 2, onde o plano de 301 define se você preserva ou queima o tráfego orgânico acumulado.

Dados estruturados: o que o core gera vs o que exige módulo, e um JSON-LD de merchant listing válido

Dados estruturados não influenciam ranqueamento diretamente, mas habilitam resultados ricos — preço, avaliação, disponibilidade — que elevam o CTR. Antes de qualquer JSON-LD, a verdade que falta em todo guia:

O que o core do Magento gera nativamente (a resposta honesta)

  • O core/Luma emite MICRODATA, não JSON-LD. O tema Luma marca a PDP com atributos itemscope/itemprop no HTML — formato legado e parcial de Product.
  • Não há merchant listing completo. Os campos exigidos para merchant listing (frete, devolução) NÃO são emitidos pelo core. JSON-LD rico exige módulo (Apptrian, Mirasvit, MagePlaza) ou template custom.
  • Misturar microdata + JSON-LD gera warning. Quando você adiciona um módulo de JSON-LD, o ideal é remover a microdata nativa dos blocos alvo (plugins de "remove native markup" fazem isso) para não duplicar structured data no Search Console.

Não há, repito, nenhuma config no Admin de dados estruturados — o que aparece depende do tema/módulo.

Onde injetar JSON-LD no Magento

O ponto de injeção correto é o layout da PDP. Você adiciona um bloco em catalog_product_view.xml apontando para um template que imprime o JSON-LD (no <head> ou logo após o corpo do produto):

<!-- app/design/frontend/Vendor/theme/Magento_Catalog/layout/catalog_product_view.xml -->
<referenceContainer name="content">
  <block class="Magento\Catalog\Block\Product\View"
         name="product.jsonld"
         template="Magento_Catalog::product/view/jsonld.phtml"/>
</referenceContainer>

Campos exigidos: Product, Offer, AggregateRating

TipoPropriedades obrigatóriasPara merchant listing também precisa
Productname, image, offerssku, brand (recomendados)
Offerprice, priceCurrency (ISO 4217), availabilityitemCondition, priceValidUntil, hasMerchantReturnPolicy, shippingDetails
AggregateRatingratingValue, reviewCountdeve refletir reviews reais visíveis

Unlike product snippets, merchant listing experiences require a price greater than zero.

Fonte: Google Search Central

JSON-LD COMPLETO para merchant listing (frete + devolução)

O esqueleto mínimo de Product que circula por aí não passa em merchant listing. Este é o que eu valido no Rich Results Test — com shippingDetails e hasMerchantReturnPolicy:

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Driven Backpack",
  "sku": "24-WB04",
  "image": "https://mystore.com/media/driven-backpack.jpg",
  "brand": { "@type": "Brand", "name": "Luma" },
  "offers": {
    "@type": "Offer",
    "url": "https://mystore.com/driven-backpack.html",
    "price": "199.90",
    "priceCurrency": "BRL",
    "priceValidUntil": "2026-12-31",
    "itemCondition": "https://schema.org/NewCondition",
    "availability": "https://schema.org/InStock",
    "hasMerchantReturnPolicy": {
      "@type": "MerchantReturnPolicy",
      "applicableCountry": "BR",
      "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
      "merchantReturnDays": 7,
      "returnMethod": "https://schema.org/ReturnByMail",
      "returnFees": "https://schema.org/FreeReturn"
    },
    "shippingDetails": {
      "@type": "OfferShippingDetails",
      "shippingRate": {
        "@type": "MonetaryAmount",
        "value": "0",
        "currency": "BRL"
      },
      "shippingDestination": {
        "@type": "DefinedRegion",
        "addressCountry": "BR"
      },
      "deliveryTime": {
        "@type": "ShippingDeliveryTime",
        "handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" },
        "transitTime": { "@type": "QuantitativeValue", "minValue": 2, "maxValue": 5, "unitCode": "DAY" }
      }
    }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.7",
    "reviewCount": "38"
  }
}

Erros literais do Rich Results Test e a causa de cada um

Erro / avisoCausa real no Magento
Missing field "offers"Template imprimiu Product sem o bloco Offer (produto sem preço carregado).
Either "offers", "review", or "aggregateRating" should be specifiedMarkup de Product sem nenhum dos três — Luma sem reviews e sem offer válido.
Missing field "shippingDetails" (aviso)JSON-LD sem OfferShippingDetails — bloqueia merchant listing rico.
Missing field "hasMerchantReturnPolicy" (aviso)Falta a política de devolução no Offer.
Invalid value in field "availability"Valor fora do vocabulário schema.org (ex.: "in stock" em vez de https://schema.org/InStock).
Invalid value in field "priceCurrency"Moeda fora do ISO 4217 (ex.: "R$" em vez de BRL).

BreadcrumbList e a relação com o Merchant Center

O BreadcrumbList substitui a URL crua por uma trilha na SERP; exige itemListElement (array de ListItem com position inteiro começando em 1, name e item). Para merchant listings consistentes, os mesmos dados de preço/frete/devolução devem bater com o seu feed do Google Merchant Center — divergência entre o JSON-LD da página e o feed gera inconsistência que o Google penaliza na elegibilidade. Valide sempre no Rich Results Test antes de subir qualquer template de PDP.

XML sitemap: config vs criação do arquivo, geração por cron, hreflang e envio ao Search Console

O ponto que confunde todo mundo: no Magento, a configuração do sitemap e a criação do arquivo ficam em telas diferentes. Errar isso é o motivo nº 1 de "meu sitemap não atualiza".

Passo 1 — Configuração (Stores > Catalog > XML Sitemap)

Em Stores > Settings > Configuration > Catalog > XML Sitemap você define o comportamento por seção (Categories / Products / CMS Pages / Store Url Options) e, crucialmente, a seção Generation Settings:

ParâmetroValoresObservação
Generation Settings > EnabledYes / Noliga a geração automática
Start TimeHH:MM:SShora em que o cron gera
FrequencyDaily / Weekly / Monthlydefina Daily para catálogo dinâmico
Priority0.0 a 1.0zero = menor prioridade
Add Images into SitemapNone / Base Only / AllAll inclui todas as imagens do produto
Maximum No of URLs per Filenuméricopadrão 50.000
Maximum File Sizebytespadrão 10.485.760 (10 MB)

For Priority, enter a value between 0.0 and 1.0. Zero has the lowest priority. [...] By default, the limit is 50,000. [...] The default size is 10,485,760 bytes.

Fonte: Adobe Experience League

Ao ultrapassar 50.000 URLs ou 10 MB, o Magento quebra automaticamente em vários arquivos com um índice — normal em catálogos grandes.

Passo 2 — Criação do arquivo (Marketing > SEO & Search > Site Map)

A configuração acima NÃO cria o arquivo sozinha. Você vai em Marketing > SEO & Search > Site Map > Add Sitemap, define o Filename (ex.: sitemap.xml) e o Path (ex.: / que resolve para pub/). Só então existe um registro de sitemap a ser (re)gerado.

O gotcha do cron

Sem cron rodando, o sitemap nunca atualiza. É o cron do Magento que regenera os arquivos no Start Time/Frequency configurados. Em servidor mal configurado (cron parado), você publica produtos e o sitemap fica congelado por dias. Confirme o cron:

# o cron precisa estar instalado no crontab do usuário web
bin/magento cron:install

# rodar manualmente para forçar (útil em deploy/debug)
bin/magento cron:run --group=index
bin/magento cron:run --group=default

Detalhe de filesystem: o arquivo é escrito no Path configurado dentro de pub/ (ex.: pub/sitemap.xml), então o diretório precisa de permissão de escrita para o usuário do PHP-FPM. Se as permissões estiverem erradas, a geração falha silenciosamente.

hreflang NO sitemap: o core não faz

Gotcha importante para quem é multi-idioma: o sitemap nativo do Magento NÃO emite xhtml:link rel="alternate" hreflang. Ele gera apenas a lista de URLs simples. Se a sua estratégia de internacionalização depende de hreflang no sitemap, você precisa de módulo ou geração custom — ver a seção de hreflang.

Envio ao Google Search Console

Atenção: a opção Enable Submission to Robots.txt apenas adiciona a linha Sitemap: https://... ao robots.txt — ela NÃO submete o sitemap ao GSC. A submissão é manual: no Search Console, vá em Sitemaps, insira a URL do sitemap.xml e envie. Depois acompanhe o status de descoberta/erros ali mesmo.

hreflang com store views: o core não gera nada e as lacunas dos módulos

A arquitetura websites → stores → store views do Magento é poderosa para multi-idioma/multi-país, mas aqui vai a verdade que destrói metade dos projetos internacionais: o core do Magento NÃO gera tags hreflang automaticamente entre store views. Não existe nada no Admin que faça isso. Você precisa de módulo (ex.: extensões "Hreflang Tags" de mercado) ou desenvolvimento custom. Quem acha que "é só ativar" descobre tarde demais que o storefront não emite uma única tag alternate.

Como o Google aceita o hreflang

  1. Tag HTML: <link rel="alternate" hreflang="pt-BR" href="url" />
  2. Header HTTP: Link: <url>; rel="alternate"; hreflang="pt-BR"
  3. Sitemap: <xhtml:link rel="alternate" hreflang="pt-BR" href="url"/> (que, lembrando, o sitemap nativo do Magento não produz)

A regra que mais gera erro: bidirecionalidade

Each language version must list itself as well as all other language versions. [...] If two pages don't both point to each other, the tags will be ignored.

Fonte: Google Search Central

Cada store view deve emitir hreflang para si mesma (auto-referência) e para todas as outras. Se a versão PT aponta para a EN, mas a EN não retribui, o Google ignora as tags inteiras.

As lacunas típicas dos módulos de hreflang (o que checar antes de confiar)

  • Não geram auto-referência: muitos módulos listam só as outras store views e esquecem a própria — quebra a bidirecionalidade.
  • Não tratam catálogo divergente: se um produto existe só na store view PT e não na EN, o módulo gera hreflang apontando para uma URL que retorna 404 na EN. Regra de ouro: não emita hreflang para página sem equivalente real.
  • Não adicionam x-default: falta o valor reservado x-default para o fallback genérico.
  • Mapeiam locale errado: o Magento usa pt_BR (underscore) no Locale do store view; o hreflang exige pt-BR (hífen). Módulo ruim copia o locale cru e o Google rejeita.

Mapeamento de Locale → código hreflang

O Locale fica em Stores > Configuration > General > General > Locale Options (escopo Store View). O de-para correto:

Store View   Locale (Magento)   hreflang (Google)
-----------  -----------------  -----------------
Português    pt_BR              pt-BR
English      en_US              en-US
Español      es_ES             es-ES   (ou es-419 p/ LatAm)
(genérico)   —                  x-default

Subfolder, subdomínio ou ccTLD

Cada store view define sua Base URL em Stores > Configuration > General > Web > Base URLs, no escopo correto (Website ou Store View). É aí que você escolhe a topologia:

  • Subfolder (loja.com/br/): mais simples, herda autoridade do domínio; meu default para multi-idioma.
  • Subdomínio (br.loja.com): separa operações, mas reparte autoridade.
  • ccTLD (loja.com.br): sinal geográfico forte, custo operacional/SEO alto (cada domínio constrói autoridade do zero).

Eu prefiro emitir o hreflang via tag HTML no <head> por ser o método mais fácil de auditar com Screaming Frog — e sempre confirmo auto-referência + x-default + ausência de hreflang para 404.

Conteúdo duplicado: multicategorias, ordenação, www/HTTPS, barra final e CMS

Conteúdo duplicado em Magento raramente é cópia de texto — é duplicação de URL. O Google oferece métodos de canonicalização com forças diferentes, e a estratégia vencedora é combinar mais de um:

MétodoForça do sinalNota prática
Redirect 301fortedestino vira canônico; transfere sinal
Redirect 302fraco p/ canonicalizaçãoNÃO consolida como o 301; use só para temporário
rel=canonicalforte (hint)sugestão, não garantia
hreflangnão canonicalizaindica versões, não consolida duplicatas
Inclusão no sitemapfracosó reforça a URL preferida

Google prefers HTTPS pages over equivalent HTTP pages as canonical, except when there are issues or conflicting signals.

Fonte: Google Search Central

Cenários típicos no Magento e como resolvo cada um

  • Produto em múltiplas categorias: ative Use Canonical Link Meta Tag for Products para consolidar tudo em domínio/url-key.html. E reveja o Use Categories Path for Product URLs (ver url_rewrite) — ligá-lo cria justamente mais variações duplicadas.
  • Parâmetros de ordenação (product_list_order/dir/limit/mode): Disallow no robots.txt; são lixo puro de duplicação (ver faceted navigation).
  • Paginação: canonical auto-referente por página, sem noindex — detalhado na seção de paginação. Não caia no anti-padrão página-1 que o core já comete.
  • www vs não-www: escolha uma forma na Base URL e force a outra via 301.
  • HTTP vs HTTPS: configure a Secure URL, force HTTP→HTTPS com 301 e ative HSTS. O Google já prefere HTTPS como canônica.
  • Barra final (trailing slash): categorias com sufixo configurado têm forma única; mas paths CMS e custom podem responder com e sem / — padronize e 301 a variante.
  • Páginas CMS duplicadas: a home costuma responder em / e /home (a CMS page padrão). Defina o canonical e/ou 301 da /home para /.

A configuração de HTTPS, HSTS e cabeçalhos seguros conecta-se ao endurecimento da loja — ver Segurança e Hardening do Magento 2. Regra de ouro: em migração eu uso 301 + canonical + sitemap atualizado simultaneamente, nunca um sinal só.

Crawl budget na prática: análise de log de servidor e Crawl Stats do Search Console

Esta é a disciplina que separa SEO técnico de verdade do resto, e que faltava neste guia. Crawl budget é finito: cada requisição que o Googlebot gasta numa faceta ?color=18&product_list_order=price é uma requisição que ele NÃO gastou num produto novo. Você não otimiza o que não mede — e a medição vem de dois lugares: o log de acesso do servidor e o Crawl Stats do Search Console.

Análise de log: onde o Googlebot realmente gasta

O log de acesso (Nginx/Apache) é a fonte da verdade — mostra o que o crawler pediu de fato, não o que você acha que ele pede. O fluxo que eu rodo: filtrar pelo user-agent do Googlebot, agrupar por padrão de URL e ver onde o budget vaza.

# 1) isolar hits do Googlebot (verifique reverse DNS para evitar fakes)
grep -i "Googlebot" /var/log/nginx/access.log > gbot.log

# 2) top 30 paths mais rastreados (vê se facetas/sort dominam)
awk '{print $7}' gbot.log | sort | uniq -c | sort -rn | head -30

# 3) quanto do crawl é desperdiçado em parâmetros
grep -c "product_list_order=" gbot.log
grep -c "?color=" gbot.log
grep -c "/catalogsearch/" gbot.log

# 4) distribuição por código de resposta (301/302/404/200)
awk '{print $9}' gbot.log | sort | uniq -c | sort -rn

Quando, em produção, eu vejo que 30-50% dos hits do Googlebot caem em URLs com parâmetro de filtro ou sort, sei imediatamente onde está o problema. Ferramentas que escalam isso: Screaming Frog Log File Analyser (importa o log e cruza com o crawl), GoAccess (dashboard em tempo real), ou o trio awk/grep/sort para auditorias rápidas.

Crawl Stats do Search Console

O complemento oficial fica em Settings (Configurações) > Crawl stats no GSC. Ele mostra, sem precisar de acesso ao servidor:

  • Total de requisições de rastreamento ao longo do tempo (tendência de subida sem motivo = facetas explodindo).
  • Por código de resposta: excesso de 404/301 indica cadeias de redirect e links quebrados consumindo budget.
  • Por tipo de arquivo: HTML vs imagem vs JS/CSS — muito crawl em JS/CSS sugere bundling ruim.
  • Por finalidade: Discovery (descoberta de URLs novas) vs Refresh (rerrastreio). Budget todo em Refresh = o Google não está achando seus produtos novos.

Como priorizar: bloquear o lixo para o budget ir ao que importa

A lógica de otimização é simples e ruthless: bloqueie o lixo no robots.txt para o crawl sobrar para produtos e categorias canônicas. Na ordem que eu executo:

  1. Disallow dos parâmetros de listagem (product_list_*) e facetas — economia imediata de crawl.
  2. Disallow de /catalogsearch/result/, ?q= e ?SID= (busca interna e sessão, que vazam para o índice).
  3. Corrigir cadeias de redirect (ver SQL na seção de url_rewrite) para não desperdiçar saltos.
  4. Garantir que a paginação seja rastreável (links reais) para o Discovery alcançar produtos profundos.
  5. Submeter sitemap atualizado via cron para guiar o crawl às URLs canônicas.

Repetir a análise de log antes e depois dessas mudanças é o que prova o ganho — o percentual de hits em URLs canônicas deve subir visivelmente em 2-4 semanas.

Search Console e CLI do Magento: o ferramental do SEO técnico em um lugar

Para fechar, dois conjuntos de ferramentas que costuram todas as seções acima: os relatórios do Google Search Console que diagnosticam os problemas, e os comandos CLI do Magento que os corrigem.

Relatórios do Search Console que importam para Magento

Relatório / statusO que significa no Magento
Indexação > "Excluída por tag canonical"Normal para facetas/variações que apontam para a categoria — sinal de que o canonical está funcionando.
"Rastreada, mas não indexada no momento"Tipicamente facetas/sort de baixo valor; bloqueie no robots para parar de gastar crawl.
"Duplicada sem canônica selecionada pelo usuário"Você esqueceu de ativar o canonical de produto/categoria, ou a paginação está canonicalizando errado.
Inspeção de URLConfere a canônica escolhida pelo Google vs a sua declarada — pega divergência de hint.
RemovalsÚnico jeito de tirar do índice, rápido, uma URL já indexada (lembre: robots.txt não desindexa).
Crawl StatsFio condutor do crawl budget (ver seção anterior).

Comandos CLI do Magento relevantes a SEO técnico

# regenerar rewrites de URL (após mudar suffix / url keys em massa)
bin/magento indexer:reindex catalog_url_rewrite

# garantir que o cron está ativo (sitemap depende dele)
bin/magento cron:install
bin/magento cron:run --group=index

# limpar cache após mudanças de config de SEO (canonical, robots, sitemap)
bin/magento cache:flush

# modo de produção: necessário p/ static-content correto e meta robots de prod
bin/magento deploy:mode:show
bin/magento deploy:mode:set production

O gotcha do deploy:mode

Atenção a um efeito colateral que pega gente em homologação: o deploy mode não muda a meta robots sozinho, mas ambientes de staging frequentemente sobem com Default Robots = NOINDEX, NOFOLLOW (em Content > Design > Configuration). Ao promover para produção, confirme que voltou para INDEX, FOLLOW — já vi loja inteira fora do índice por semanas porque esqueceram esse campo no go-live. E lembre que o robots.txt do Admin (Content > Design > Configuration > Search Engine Robots) é ignorado se existir um arquivo físico robots.txt em pub/ — o servidor serve o arquivo e o Admin vira decorativo. Em multi-site, use arquivos por domínio e nunca exponha o path do Admin em Disallow.

Perguntas frequentes

Por que minhas URLs de filtro no Magento não são ?color=blue como mostram os tutoriais?

Porque o módulo Magento_LayeredNavigation usa o attribute_code do atributo como nome do parâmetro e o ID da opção como valor — então a URL real é /bags.html?color=18 (onde 18 é o option_id de 'Blue'), não o texto. Atributos de Price/Decimal entram como faixa (?price=100-200). Por isso, ao escrever o robots.txt, você precisa listar os attribute_code do SEU catálogo, não nomes inventados. Se um módulo de SEO (Mirasvit/Amasty) reescreveu o parâmetro como alias textual ou path, audite a URL renderizada antes de criar qualquer regra de Disallow.

Posso usar robots.txt Disallow e meta noindex juntos na mesma página de filtro?

Não — são mutuamente exclusivos. Se você bloqueia a URL no robots.txt, o Googlebot nunca baixa a página e nunca lê o noindex nem o canonical, então o noindex vira letra morta. Pior: o Disallow não remove do índice o que já foi indexado (a URL pode continuar aparecendo sem snippet). A decisão é binária: para lixo novo (sort/dir/limit/mode), use Disallow para nunca gastar crawl; para desindexar algo já no índice, deixe rastreável, sirva noindex, espere cair, e só então bloqueie no robots. Para remoção rápida do que já indexou, use a ferramenta Removals do Search Console.

O canonical nativo do Magento trata a paginação corretamente?

Não. O canonical nativo gera, em todas as páginas paginadas (?p=2, ?p=3...), um canonical apontando para a categoria base sem o ?p — exatamente o anti-padrão 'canonical de todas as páginas para a página 1' que o Google desaconselha (documentado na issue #37338, confirmada e reproduzida em 2.4.x). O efeito é o Google ignorar produtos que só existem da página 2 em diante. A correção é canonical auto-referente por página (?p=2 canonicaliza para ?p=2), links de paginação rastreáveis e SEM noindex — o que normalmente exige módulo de SEO ou plugin, já que o core não oferece esse controle granular. Lembre também que rel=next/prev foi descontinuado pelo Google em março de 2019.

O Magento gera dados estruturados (JSON-LD) de produto sozinho?

Apenas parcialmente, e em formato legado. O tema Luma emite microdata (itemscope/itemprop) de Product de forma limitada; não há JSON-LD completo nem merchant listing nativo. Os campos de merchant listing — shippingDetails e hasMerchantReturnPolicy, além de itemCondition e priceValidUntil — não saem do core. Para JSON-LD rico você precisa de módulo (Apptrian, Mirasvit, MagePlaza) ou template custom injetado via catalog_product_view.xml, idealmente removendo a microdata nativa para não duplicar markup no Search Console. Valide sempre no Rich Results Test e mantenha os dados de preço/frete coerentes com o feed do Google Merchant Center.

Por que meu XML sitemap do Magento não atualiza?

Quase sempre é cron parado. No Magento, a configuração fica em Stores > Configuration > Catalog > XML Sitemap (Generation Settings: Enabled, Start Time, Frequency), mas o arquivo só é criado depois que você adiciona um sitemap em Marketing > SEO & Search > Site Map > Add Sitemap. É o cron do Magento que regenera o arquivo no horário configurado — sem cron rodando (verifique com bin/magento cron:install), ele congela. Confira também a permissão de escrita no diretório de destino dentro de pub/. E note: Enable Submission to Robots.txt só adiciona a linha Sitemap: ao robots, não submete ao GSC; a submissão no Search Console é manual, em Sitemaps.

O Magento cria as tags hreflang automaticamente entre as store views?

Não. O core do Magento não emite nenhuma tag hreflang — é preciso módulo ou desenvolvimento custom. E, mesmo com módulo, cheque as lacunas comuns: muitos não geram a auto-referência (quebrando a bidirecionalidade que o Google exige), apontam hreflang para uma URL que retorna 404 quando o produto não existe naquela store view, esquecem o x-default, e copiam o locale cru pt_BR em vez de mapear para pt-BR. A regra de ouro é não emitir hreflang para páginas sem equivalente real na outra store view, e sempre incluir a auto-referência.

Como descubro onde o Googlebot está desperdiçando crawl budget na minha loja?

Cruzando o log de acesso do servidor com o Crawl Stats do Search Console. No log, filtre os hits do Googlebot (grep Googlebot) e agrupe por path (awk '{print $7}' | sort | uniq -c | sort -rn) para ver se facetas e parâmetros de sort dominam; conte quantos hits caem em product_list_order=, ?color= e /catalogsearch/. No GSC, vá em Settings > Crawl stats para ver total de requisições, distribuição por código de resposta e a divisão Discovery vs Refresh. Quando 30-50% do crawl cai em URLs com parâmetro, a prioridade é bloquear esse lixo no robots.txt para o budget ir a produtos e categorias canônicas — e repetir a análise depois para provar o ganho.

Referências oficiais

  1. Managing crawling of faceted navigation URLs — Google Search Central
  2. Block search indexing with noindex (robots.txt e noindex são exclusivos) — Google Search Central
  3. Categories do not follow Google's best practices for pagination (Issue #37338) — magento/magento2 (GitHub)
  4. Use Canonical Link Meta Tag (merchdocs) — Magento merchdocs (GitHub)
  5. Automatic product redirects (Create Permanent Redirect) — Adobe Experience League
  6. XML Sitemap - Adobe Commerce Admin — Adobe Experience League
  7. Intro to Product structured data on Google — Google Search Central
  8. Merchant listing (structured data) experiences — Google Search Central
  9. Breadcrumb (BreadcrumbList) structured data — Google Search Central
  10. Consolidate duplicate URLs (canonicalization) — Google Search Central
  11. Tell Google about localized versions (hreflang) — Google Search Central
  12. Pagination with rel=next and rel=prev (histórico/descontinuação) — Google Search Central Blog
  13. Crawl Stats report (Search Console) — Google Search Console Help
  14. Google does not use the keywords meta tag in web ranking — Google Search Central Blog
Precisa de um orçamento? Ficarei feliz em ajudar. Clique Aqui