Skip to content

Cannot enforce 301 redirect for multiple slashes due to CDN path normalization #3520

@IvanKalynych

Description

@IvanKalynych

What is the location of your example repository?

No response

Which package or tool is having this issue?

Oxygen

What version of that package or tool are you using?

"vite": "^6.2.4",

What version of Remix are you using?

"react-router": "7.9.2",

Steps to Reproduce

When requesting:

https://pick-up-shoes.com.ua///////

the response is:

HTTP/2 200

instead of a 301 redirect to:

https://pick-up-shoes.com.ua/

We have URL normalization logic in server.ts (Hydrogen app) that:

collapses multiple slashes

removes trailing slash (except root)

forces lowercase

issues 301 when the normalized path differs

However, the redirect is never triggered for ///////.

// server.ts
// Virtual entry point for the app
import {storefrontRedirect} from '@shopify/hydrogen';
import {createRequestHandler} from '@shopify/hydrogen/oxygen';
import {createHydrogenRouterContext} from '~/lib/context';

/**

  • Export a fetch handler in module format.
    */
    export default {
    async fetch(
    request: Request,
    env: Env,
    executionContext: ExecutionContext,
    ): Promise {
    try {
    const url = new URL(request.url);

    let normalizedPath = url.pathname.replace(//{2,}/g, '/');

    if (normalizedPath !== '/' && normalizedPath.endsWith('/')) {
    normalizedPath = normalizedPath.slice(0, -1);
    }

    normalizedPath = normalizedPath.toLowerCase();

    if (url.pathname !== normalizedPath) {
    const redirectUrl = ${url.origin}${normalizedPath}${url.search};
    return Response.redirect(redirectUrl, 301);
    }

    const hydrogenContext = await createHydrogenRouterContext(
    request,
    env,
    executionContext,
    );

    /**
    * Create a Remix request handler and pass
    * Hydrogen's Storefront client to the loader context.
    */
    const handleRequest = createRequestHandler({
    // eslint-disable-next-line import/no-unresolved
    build: await import('virtual:react-router/server-build'),
    mode: process.env.NODE_ENV,
    getLoadContext: () => hydrogenContext,
    });

    const response = await handleRequest(request);

    if (hydrogenContext.session.isPending) {
    response.headers.set(
    'Set-Cookie',
    await hydrogenContext.session.commit(),
    );
    }

    if (response.status === 404) {
    /**
    * Check for redirects only when there's a 404 from the app.
    * If the redirect doesn't exist, then storefrontRedirect
    * will pass through the 404 response.
    */
    return storefrontRedirect({
    request,
    response,
    storefront: hydrogenContext.storefront,
    });
    }

    return response;
    } catch (error) {
    console.error(error);
    return new Response('An unexpected error occurred', {status: 500});
    }
    },
    };

Expected Behavior

301 redirect to:

https://pick-up-shoes.com.ua/

Actual Behavior

When requesting:

https://pick-up-shoes.com.ua///////

the response is:

HTTP/2 200

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions