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.
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=Naponte 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,alldestró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.
| Campo | Default | Quando "Yes" | Exemplo gerado |
|---|---|---|---|
| Use Canonical Link Meta Tag for Categories | No | Canonical para o caminho completo da categoria | mystore.com/gear/bags/ |
| Use Canonical Link Meta Tag for Products | No | Canonical no formato domínio + url-key | mystore.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=2aponta 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:
| Coluna | O que guarda |
|---|---|
url_rewrite_id | PK auto-incremento |
entity_type | product | category | cms-page | custom |
entity_id | ID da entidade (product_id, category_id, page_id...) |
request_path | URL solicitada (ex.: driven-backpack.html) |
target_path | Destino interno ou URL final (ex.: catalog/product/view/id/42) |
redirect_type | 0 = sem redirect (rewrite interno) · 301 = permanente · 302 = temporário |
store_id | Escopo de store view (rewrites diferentes por idioma) |
is_autogenerated | 1 = gerado pelo sistema · 0 = override manual (Marketing) |
metadata | Metadados (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:flushUm 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/itempropno HTML — formato legado e parcial deProduct. - 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
| Tipo | Propriedades obrigatórias | Para merchant listing também precisa |
|---|---|---|
| Product | name, image, offers | sku, brand (recomendados) |
| Offer | price, priceCurrency (ISO 4217), availability | itemCondition, priceValidUntil, hasMerchantReturnPolicy, shippingDetails |
| AggregateRating | ratingValue, reviewCount | deve 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 / aviso | Causa 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 specified | Markup 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âmetro | Valores | Observação |
|---|---|---|
| Generation Settings > Enabled | Yes / No | liga a geração automática |
| Start Time | HH:MM:SS | hora em que o cron gera |
| Frequency | Daily / Weekly / Monthly | defina Daily para catálogo dinâmico |
| Priority | 0.0 a 1.0 | zero = menor prioridade |
| Add Images into Sitemap | None / Base Only / All | All inclui todas as imagens do produto |
| Maximum No of URLs per File | numérico | padrão 50.000 |
| Maximum File Size | bytes | padrã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=defaultDetalhe 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
- Tag HTML:
<link rel="alternate" hreflang="pt-BR" href="url" /> - Header HTTP:
Link: <url>; rel="alternate"; hreflang="pt-BR" - 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-defaultpara o fallback genérico. - Mapeiam locale errado: o Magento usa
pt_BR(underscore) no Locale do store view; o hreflang exigept-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-defaultSubfolder, 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étodo | Força do sinal | Nota prática |
|---|---|---|
| Redirect 301 | forte | destino vira canônico; transfere sinal |
| Redirect 302 | fraco p/ canonicalização | NÃO consolida como o 301; use só para temporário |
| rel=canonical | forte (hint) | sugestão, não garantia |
| hreflang | não canonicaliza | indica versões, não consolida duplicatas |
| Inclusão no sitemap | fraco | só 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/homepara/.
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 -rnQuando, 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:
- Disallow dos parâmetros de listagem (
product_list_*) e facetas — economia imediata de crawl. - Disallow de
/catalogsearch/result/,?q=e?SID=(busca interna e sessão, que vazam para o índice). - Corrigir cadeias de redirect (ver SQL na seção de url_rewrite) para não desperdiçar saltos.
- Garantir que a paginação seja rastreável (links reais) para o Discovery alcançar produtos profundos.
- 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 / status | O 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 URL | Confere 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 Stats | Fio 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 productionO 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
- Managing crawling of faceted navigation URLs — Google Search Central
- Block search indexing with noindex (robots.txt e noindex são exclusivos) — Google Search Central
- Categories do not follow Google's best practices for pagination (Issue #37338) — magento/magento2 (GitHub)
- Use Canonical Link Meta Tag (merchdocs) — Magento merchdocs (GitHub)
- Automatic product redirects (Create Permanent Redirect) — Adobe Experience League
- XML Sitemap - Adobe Commerce Admin — Adobe Experience League
- Intro to Product structured data on Google — Google Search Central
- Merchant listing (structured data) experiences — Google Search Central
- Breadcrumb (BreadcrumbList) structured data — Google Search Central
- Consolidate duplicate URLs (canonicalization) — Google Search Central
- Tell Google about localized versions (hreflang) — Google Search Central
- Pagination with rel=next and rel=prev (histórico/descontinuação) — Google Search Central Blog
- Crawl Stats report (Search Console) — Google Search Console Help
- Google does not use the keywords meta tag in web ranking — Google Search Central Blog