404 en las fichas de producto tras pasar a PrestaShop 9

por Luca Cavallini Casos de soporte
404 en las fichas de producto tras pasar a PrestaShop 9

El síntoma

Tras pasar a PrestaShop 9, las fichas de producto a las que se llega por las direcciones antiguas, las que llevan el nombre de la categoría delante del ID (/categoria/12-nombre-producto.html), responden 404. Las direcciones sin categoría (/12-nombre-producto.html) funcionan y la tienda se navega con normalidad. El daño llega desde fuera: los enlaces de otros sitios, los marcadores y las páginas que Google ya tiene indexadas siguen apuntando a las direcciones antiguas, y en la próxima visita encuentran una página de error en lugar del producto.

Qué parecía y no era

La primera sospecha, ante un 404 tras una migración, suele ser el .htaccess: reglas de reescritura perdidas, mod_rewrite apagado. Aquí no es eso: la petición llega a PrestaShop, y la página que se ve es la página «no encontrado» del tema, generada por la tienda.

La segunda sospecha es el nombre del producto: si el link_rewrite ha cambiado, la dirección ya no coincide. Pero en ese caso PrestaShop no responde 404. Con el ID correcto y el nombre equivocado redirige con un 301 a la dirección correcta, siempre que la redirección canónica esté activa con la opción «301 Moved Permanently» (PS_CANONICAL_REDIRECT = 2). El 404 solo aparece cuando la dirección tiene una forma que ninguna regla reconoce.

La causa real

PrestaShop construye y reconoce las direcciones con una regla por cada tipo de página. Para los productos, la regla predeterminada en las versiones 1.7 y 8 es esta (la forma de 1.7.1.2, 1.7.8.10 y 8.1):

{category:/}{id}{-:id_product_attribute}-{rewrite}{-:ean13}.html

es decir, el nombre de la categoría predeterminada del producto, luego el ID, la combinación si la hay, el nombre y el EAN13 si lo hay. En la 9.0 la regla predeterminada, en el archivo classes/Dispatcher.php, pasó a ser:

{id}{-:id_product_attribute}-{rewrite}.html

Sin categoría y sin EAN13. Lo hemos verificado comparando los códigos fuente de 1.7.1.2, 1.7.8.10 y 8.1 (regla con la categoría) con los de 9.0.0 y 9.1.4 (regla sin ella). Es la única regla predeterminada que cambia entre la 8.1 y la 9.0, y el cambio figura en el registro de cambios oficial de la 9.0 (n.º 37467, «SEO delete category in URL product scheme during installation»).

La regla predeterminada solo vale mientras nadie haya guardado una. El Dispatcher carga primero las reglas predeterminadas y luego las sobrescribe con las guardadas en la tabla ps_configuration, con claves como PS_ROUTE_product_rule y PS_ROUTE_category_rule: son los valores de la página SEO y URLs del back office. Esas claves aparecen cuando alguien guarda esa página: en una tienda que nunca la ha guardado suelen faltar. En nuestra tienda, que funciona con la 9.1, no hay ni una. Una tienda que nunca ha guardado la página usa la regla predeterminada de la versión que se está ejecutando: al cambiar de versión cambian las direcciones, sin que nadie haya tocado nada.

El proyecto PrestaShop lo reconoció como un problema serio de SEO. El informe n.º 40063 («Legacy Category/Product URLs Return 404 With New Default Routing») se cerró el 26 de noviembre de 2025 con una corrección en el módulo de actualización oficial, la PR n.º 1535 («Persist current product route when upgrading to 9.0.0»), que guarda la ruta en uso cuando no hay ninguna. La corrección protege a quien actualiza con ese módulo. No protege a quien monta una instalación nueva de PrestaShop 9 y le importa los datos de la tienda antigua, que es la migración descrita en el informe (de 1.6.1.4 a 9.0.1, con instalación limpia e importación): ahí el esquema es el nuevo desde el primer día.

La prueba, hecha el 9 de octubre de 2026 en PrestaShop 9.1 sin reglas guardadas (una copia de prueba de nuestra tienda, más peticiones de solo lectura en la propia tienda), con el producto 2 y su categoría predeterminada compliance-ue:

  • /2-eu-withdrawal-button.html: 200.
  • /compliance-ue/2-eu-withdrawal-button.html: 404.
  • /2-otro-nombre.html (ID correcto, nombre equivocado): 301 a /2-eu-withdrawal-button.html.

La corrección

Hay dos caminos. El A devuelve el esquema antiguo y funciona incluso si el sitio con las direcciones nuevas ya está en línea. El B mantiene el esquema nuevo, más corto, y redirige las direcciones antiguas.

Camino A: volver al esquema antiguo.

  1. En el back office: Parámetros de la tienda, Tráfico & SEO, SEO y URLs, sección «Formato de los enlaces», campo «Ruta a los productos». En el código, ese campo del formulario está vinculado a la clave PS_ROUTE_product_rule.
  2. Introducir la regla que usaba la tienda antigua. Si nunca se había modificado, es la predeterminada hasta la 8.x: {category:/}{id}{-:id_product_attribute}-{rewrite}{-:ean13}.html. Si estaba personalizada, hay que volver a poner esa.
  3. Guardar y probar un producto.

Hemos probado el efecto escribiendo esa clave en la copia de prueba (no desde el formulario, pero es la clave que el formulario escribe). La dirección antigua con la categoría vuelve a responder 200. La dirección sin categoría, que alguien pudo enlazar mientras tanto, pasa a ser un 301 hacia la que lleva la categoría, no un 404. Una categoría equivocada en la dirección también se corrige con un 301.

Camino B: mantener el esquema nuevo y redirigir las direcciones antiguas. En Apache, en el archivo .htaccess, antes del bloque comprendido entre los comentarios «~~start~~» y «~~end~~» de PrestaShop: lo que queda fuera de ese bloque PrestaShop lo conserva cuando regenera el archivo (lo dice el propio comentario). Sustituye la lista de idiomas por los prefijos de tu propia tienda:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(it|en|fr|es|de|pl|nl)/[^/]+/([0-9]+(-[0-9]+)?-[^/]+\.html)$ https://%{HTTP_HOST}/$1/$2 [R=301,L]
RewriteCond %{REQUEST_URI} !^/(it|en|fr|es|de|pl|nl)/
RewriteRule ^[^/]+/([0-9]+(-[0-9]+)?-[^/]+\.html)$ https://%{HTTP_HOST}/$1 [R=301,L]
</IfModule>

La primera regla reescribe las direcciones con prefijo de idioma y categoría, la segunda las del idioma predeterminado, que en nuestra tienda no lleva prefijo. La condición intermedia evita tocar las direcciones nuevas de la forma /en/12-nombre.html. Probado en la copia de prueba: /compliance-ue/2-eu-withdrawal-button.html y /en/compliance-ue/2-eu-withdrawal-button.html responden 301 hacia las direcciones sin categoría, mientras que /2-eu-withdrawal-button.html, /en/2-eu-withdrawal-button.html y una página CMS siguen en 200. Si el nombre del producto ha cambiado mientras tanto, el segundo salto lo hace PrestaShop con la redirección canónica.

Cómo comprobar que aguanta, y cómo evitarlo

  • Antes de tocar nada, lee lo que hay guardado: SELECT name, value FROM ps_configuration WHERE name LIKE 'PS_ROUTE%'; (con el prefijo de tablas de tu propia instalación). Ninguna fila significa que la tienda usa las reglas predeterminadas de la versión instalada.
  • Toma una muestra de direcciones antiguas de producto de la sitemap antigua o de Search Console y comprueba su código de respuesta con curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' DIRECCION. Sirven 200 o un 301 hacia la ficha correcta. Nunca 404.
  • Prueba también un producto con combinaciones, cuya dirección contiene el ID de la combinación (/12-3-nombre.html), y repite la prueba con el prefijo de cada idioma.
  • Tras cada cambio de esquema, regenera la sitemap: es un archivo escrito por el módulo en el momento de la generación, y contiene las direcciones del esquema en uso ese día.
  • Antes de migrar, copia la regla de producto en uso del campo «Ruta a los productos» y consérvala en el proyecto. Si montas una instalación nueva e importas los datos, define y guarda esa regla antes de abrir el sitio: desde ese momento es un valor guardado y ya no depende del valor predeterminado de la versión.
  • Si actualizas con el módulo de actualización, prueba primero en una copia y compara el campo «Ruta a los productos» antes y después, que es también la comprobación que indica la PR de la corrección.

Si prefieres gestionar las redirecciones desde el back office en lugar de en el .htaccess, puedes usar cll_seoredirect, nuestro módulo de redirecciones 301/302 y de seguimiento de los 404: acepta reglas con expresiones regulares y sustitución de grupos ($1), de modo que la regla del camino B se escribe allí igual. La hemos probado en la copia de prueba. El problema y las correcciones descritas aquí valen igualmente con cualquier herramienta.

Etiquetas: redirect seo

Deja un comentario

Los comentarios se moderan antes de su publicación.

← Blog

Cargando...
Volver arriba