商品地址原本是 red-shirt.html,保存后变成 red-shirt-1.html,再次保存又出现新的后缀。Magento 这样做通常是为了避免同一店铺下的 request_path 冲突。直接改 url_key 只能绕开表面冲突,还可能留下更多历史重写。

从目标路径反查占用者

SELECT url_rewrite_id, entity_type, entity_id, request_path,
       target_path, redirect_type, store_id, is_autogenerated
FROM url_rewrite
WHERE request_path IN ('red-shirt.html','red-shirt-1.html')
ORDER BY store_id, request_path;

同一个 request_path 可以存在于不同 store_id,但不能在同一店铺范围重复。重点判断冲突记录属于另一个商品、分类路径、CMS 自定义重写,还是该商品自己的旧 301。

商品层面的 url_key 是否被 Store View 覆盖

SELECT a.attribute_id, a.attribute_code
FROM eav_attribute a
JOIN eav_entity_type t ON t.entity_type_id=a.entity_type_id
WHERE t.entity_type_code='catalog_product' AND a.attribute_code='url_key';

SELECT value_id, attribute_id, store_id, entity_id, value
FROM catalog_product_entity_varchar
WHERE entity_id=123 AND attribute_id=117;

这里的 attribute_id 必须使用第一条查询实际返回值,不能照抄示例。store_id=0 是默认值,其他记录是店铺视图覆盖。后台切错范围时,你修改的值可能并不是前台正在使用的那个。

决定保留历史跳转还是清理错误记录

正常的 URL 变更会生成旧地址到新地址的 301,这对 SEO 有价值。只有确认某条记录由失败导入、错误店铺范围或已删除实体遗留造成,才备份后定向处理。

CREATE TABLE url_rewrite_backup_20250130 AS
SELECT * FROM url_rewrite
WHERE store_id=1 AND request_path LIKE 'red-shirt%';

SELECT * FROM url_rewrite
WHERE store_id=1 AND entity_type='product' AND entity_id=123;

不要执行无条件 TRUNCATE url_rewrite。它会同时清掉自定义重写和历史跳转,重建后也未必恢复人工规则。

修复后的检查不止打开新 URL

curl -skI https://shop.example.com/red-shirt.html
curl -skI https://shop.example.com/red-shirt-old.html
bin/magento cache:clean full_page

新地址应返回 200,确定需要保留的旧地址应只跳转一次并到达新地址,不能形成 301 链或循环。最后再保存商品一次,如果 request_path 保持稳定,才说明冲突已真正解除。