商品 URL Key 保存后变成 blue-shirt-1,很多人的第一反应是“肯定还有一个 blue-shirt 商品”。但我处理过的几次,商品名和 URL Key 都没有重复,真正占坑的是 url_rewrite 里的历史记录。

Magento 最终要求唯一的是某个 Store 下的 request_path,而不是后台表单里那一格 URL Key。分类路径、URL 后缀、旧商品的永久重定向,都会参与生成结果。

先按最终路径搜,不要只搜商品属性

假设目标路径是 blue-shirt.html,先查对应 Store:

SELECT url_rewrite_id, entity_type, entity_id, request_path,       target_path, redirect_type, store_id, is_autogenerated
FROM url_rewrite
WHERE store_id = :store_id  AND request_path IN ('blue-shirt', 'blue-shirt.html');

如果商品开启了“Use Categories Path for Product URLs”,还要搜索带分类前缀的路径。反过来也一样:你在后台看到的 key 没有分类,但生成 request_path 时可能已经加了分类层级。

看到冲突记录后,先判断它为什么存在

我一般把结果分成三类:

  • 仍指向有效商品或分类:这是正常占用,应改新商品 URL,不能删除别人的入口;
  • 301 历史重定向:说明这个地址过去用过。删除会让旧链接失去 SEO 价值,要先确认业务是否允许复用;
  • 孤儿或重复生成记录:entity 已不存在,或者一次失败导入留下异常数据。这才是需要进一步修复的对象。

查孤儿时也不要只写一个大 DELETE。先把目标范围导出来,至少保留 url_rewrite_id、路径、Store 和目标实体。多商店尤其容易误删:同一路径在不同 store_id 下可以分别存在,问题只发生在保存商品的那个 Store。

为什么删完又回来

如果记录的 is_autogenerated = 1,它通常由目录 URL 重写逻辑重新生成。你只删结果,不改产生冲突的商品、分类路径或作用域,下一次保存、导入或重建时它还会回来。

另一个隐蔽点是 Store View 回退。默认作用域里是 blue-shirt,某个 Store View 曾单独保存过同样的值,后来界面上又切回“Use Default Value”。排查时应该明确列出每个作用域的实际属性值,而不是只看当前后台页面。

我的处理顺序

  1. 确定目标 Store、URL 后缀以及是否带分类路径;
  2. 在 url_rewrite 查最终 request_path 的占用者;
  3. 打开占用者,确认它是有效入口、历史 301 还是孤儿;
  4. 能从后台调整的,优先修改源实体并让 Magento 重建;
  5. 只有确认是异常数据时,才在备份后做定向清理,再保存单个实体验证。

我不会直接清空整个 url_rewrite 表。那样确实可能让当前商品拿回漂亮 URL,却同时丢掉全站自定义重写和历史重定向。这个问题的关键不是“怎样去掉 -1”,而是先回答:没有 -1 的那个地址,现在究竟属于谁。