{"title":"Match legacy URLs that end in a file extension","slug":"legacy-url-extensions","url":"https://support.storeconnect.com/articles/legacy-url-extensions","url_markdown":"https://support.storeconnect.com/articles/legacy-url-extensions.md","subtitle":null,"summary":"Turn on legacy URL extension matching so old URLs such as /about/team.html reach the same content at /about/team, with a 301 redirect by default. Use it after migrating a site whose indexed URLs carry a file extension, instead of creating a route mapping for every page.","type":"Help_Documentation","video_url":"","keywords":"legacy url, file extension, .html, .htm, site migration, 301 redirect, 302 redirect, routing.matchable_url_extensions, routing.matchable_url_status, extensionless url, old urls, 404, route mapping, seo","last_modified":"2026-10-23T02:15:22+0000","body_markdown":"Use this process when you have moved a site to StoreConnect on the same domain and the old site's URLs end in a file extension, such as `/about/team.html`. StoreConnect serves the same content at `/about/team`, so without extension matching every old URL returns a 404.\n\nExtension matching treats the listed extensions as optional. A request for `/about/team.html` that would otherwise 404 is sent to `/about/team`, as long as that page exists. It replaces a [route mapping](route-mapping) for each old URL.\n\n## How matching works\n\n- **Off by default.** A store with no extensions listed is unaffected.\n- **Only after a 404.** A page that really exists at `/foo.html` is served as normal.\n- **Route mappings take precedence.** If a **Route Mapping** exists for the requested URL, that mapping applies instead.\n- **Content pages and product categories only.** The extensionless path must be a visible content page, or a product category whose whole path exists. For example, `/womens/shoes.html` matches only when the **shoes** category sits under **womens**. If neither applies, the 404 stands, so a visitor is never redirected to a missing page. Product pages need no matching or route mapping: `/products/\u003cslug\u003e.html` already serves the product page directly with a 200, whether or not extension matching is on, and the canonical tag still points at `/products/\u003cslug\u003e`.\n- **Final extension only.** `/page.html.old` does not match `html`, because the last extension is `old`.\n- **Not case sensitive.** `.HTML` matches `html`.\n- **Query string kept.** `/about/team.html?ref=email` redirects to `/about/team?ref=email`.\n\n## Turn on extension matching\n\n1.  Open the **Store** record and go to the **Store Variables** related list.\n2.  Create a store variable with the **Key** `routing.matchable_url_extensions` and the extensions as the **Value**, separated by commas. For example, `html,htm`. A leading dot is ignored, so `.html` also works.\n3.  (Optional) Create a store variable with the **Key** `routing.matchable_url_status` to change the response. Leave it unset to redirect with a `301`.\n\n    | Value | Response |\n    |---|---|\n    | `301` (default) | Permanent redirect to the extensionless URL. Search engines move the old URL's ranking to the new one. |\n    | `302` | Temporary redirect to the extensionless URL. |\n    | `200` | Serves the page at the old URL with no redirect. The page's canonical tag still points at the extensionless URL. |\n\n    Any other value is treated as `301`.\n\n4.  Open an old URL on your storefront, such as `https://shop.example.com/about/team.html`.\n\nThe change applies from the next request. With the default response, the browser lands on `https://shop.example.com/about/team` and the request returns a `301`.\n\n:::note\nIf category URLs on your store follow the category tree (the `catalog.category_paths.hierarchical` store variable), a match on a single-segment category path redirects twice: first to the extensionless path, then to the full category path.\n:::\n\n## Turn it off\n\nDelete the `routing.matchable_url_extensions` store variable, or clear the **Value**. Old URLs with an extension return a 404 again. Search engines and browsers may keep a `301` they have already seen, so use `302` while you are still testing."}