BUG REPORT: Redirect Admin API (POST /redirects.json) mangling URLs

Topic summary

Initial Issue:
The Shopify Redirect Admin API (POST /redirects.json) appeared to be removing URL-encoding from target URLs. When submitting /?foo=%3F, the API returned /?foo=?, seemingly stripping the encoding and potentially creating invalid URLs.

Attempted Workarounds:

  • The GraphQL mutation for creating redirects was considered but requires undocumented app scopes (making it unavailable)
  • GraphQL API is only available for Shopify Plus stores
  • Issue persisted across API versions (2022-04 and 2022-10)

Resolution:
After further testing, the original reporter determined this is not a bug. Shopify parses and re-encodes URLs using an algorithm that minimizes encoding while maintaining spec compliance:

  • Question marks (%3F) don’t require encoding in query values since only the first ? acts as a delimiter
  • Ampersands (%26) are re-encoded because they serve as delimiters in query strings

Key Takeaway:
Decoding URL-encoded strings before sending to the API is not a solution and will cause issues. For example, decoding /?foo=%26 to /?foo=& creates /?foo=&, where the ampersand becomes a delimiter and breaks the intended value. The encoding exists for a reason and should be preserved.

Summarized with AI on November 24. AI used: claude-sonnet-4-5-20250929.

REQUEST -----------------------------------------

version: 2022-04

path: /redirects.json

body:

{
“path”: “tttsd3r”,
“target”: “/?foo=%3F”
}

RESPONSE -----------------------------------------

code: 201

x-request-id: e2c9b012-5baa-42ca-add0-8554462b145b

body:

{
“id”: 385679786197,
“path”: “/tttsd3r”,
“target”: “/?foo=?”
}


Shopify backend seems to remove URL-encoding from the provided target string, making the resulting target URL invalid. Also tested with latest API version 2022-10.

I also tried using GraphQL to get around this bug, but the corresponding GraphQL mutation seems to be unavailable because it requires an undocumented (presumably unavailable) app scope:

https://shopify.dev/api/admin-graphql/2022-04/mutations/urlredirectcreate

This issue may be occurring because the API is not properly handling the URL-encoded characters in the target string. In your request, the target string contains a URL-encoded question mark (%3F), which should be decoded to a regular question mark (?) in the resulting URL. However, it appears that the API is removing the URL-encoding and returning the target string with a literal %3F instead of a decoded ?, which results in an invalid URL.

One potential solution to this issue would be to manually decode the URL-encoded characters in the target string before sending your request to the API. For example, you could use the decodeURIComponent function in JavaScript to decode the target string before sending your request. This would ensure that the API receives a valid, decoded target string and returns a valid URL in the response.

Alternatively, you could try using the GraphQL mutation for creating URL redirects, as you mentioned. This mutation is available in the Shopify Admin GraphQL API and does not require any additional app scopes. However, keep in mind that the GraphQL API is only available for stores on the Shopify Plus plan.

I hope this information is helpful and resolves the issue you are encountering with creating URL redirects using the Shopify API. If you have any further questions, please don’t hesitate to ask.

I did some more testing and realized that this is not a bug. What is happening is that Shopify is parsing the provided URL and re-encoding it using an algorithm that aims to encode as few characters as possible while staying within the URL spec.

In the example above, we see that the question mark is not URL-encoded, as it turns out, this is valid according to the spec because it is unambiguous, only the first question mark is treated as a delimiter. However, when I run the same test with a URL-encoded ampersand (/?foo=%26), Shopify re-encodes it in the returning URL. Unlike a question mark, an ampersand is a delimiter in the URL query string.

One potential solution to this issue would be to manually decode the URL-encoded characters in the target string before sending your request to the API. For example, you could use the decodeURIComponent function in JavaScript to decode the target string before sending your request. This would ensure that the API receives a valid, decoded target string and returns a valid URL in the response.

For anyone reading this in the future, this is not a solution. The string is encoded for a reason, decoding it on your end will render your URL invalid even before you send it to Shopify. For example, decoding /?foo=%26 will result in /?foo=& and the ampersand will effectively get lost in the foo value as it will be treated as a delimiter.