Paul LovellBook a call
Back to Blog
9 min readPaul Lovell

How to Host Shopify or Zendesk in a Subfolder Using Cloudflare Workers

"Our tech stack won't allow a subfolder" is usually solvable with a reverse proxy at the edge, not a platform change.

Technical SEOSite Architecture

One of the most common objections to consolidating content into subfolders is that the platform forces a subdomain — Shopify and Zendesk both default to their own subdomain-style URLs. A reverse proxy solves this without migrating platforms: intercept requests at the edge (Cloudflare Workers or NGINX) and route a subfolder path to the third-party origin, while the URL a user or crawler sees stays on your root domain.

User / Googlebot → request: example.com/shop/item-1
                          [Cloudflare Worker at the edge]
                     ┌──────────────┴──────────────┐
                path: /blog/*                 path: /shop/*
        forward to WordPress origin      forward to Shopify origin

Why this matters more than it looks

A subdomain is treated as a distinct host for most authority-consolidation purposes, which is why a shop.example.com or support.example.com setup tends to build its own separate ranking history instead of reinforcing the root domain's. Routing that same content through a subfolder path keeps backlinks, crawl equity, and topical relevance pooled under one domain name — the reverse proxy is what makes that possible without asking Shopify or Zendesk to natively support subfolder hosting, which neither platform does out of the box.

The worker script

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const url = new URL(request.url)

  if (url.pathname.startsWith('/shop')) {
    const targetOrigin = 'https://your-store.myshopify.com'
    const newUrl = new URL(targetOrigin + url.pathname + url.search)

    const modifiedHeaders = new Headers(request.headers)
    modifiedHeaders.set('X-Forwarded-Host', url.hostname)
    modifiedHeaders.set('Host', 'your-store.myshopify.com')

    const modifiedRequest = new Request(newUrl, {
      method: request.method,
      headers: modifiedHeaders,
      body: request.body,
      redirect: 'manual'
    })

    return fetch(modifiedRequest)
  }

  return fetch(request) // default: pass through to the main site origin
}

The NGINX alternative

If the origin isn't sat behind Cloudflare, the same proxy pattern works as a server block. This is the setup to use when the domain name's DNS is managed elsewhere and adding Cloudflare as the edge layer isn't an option:

location /shop/ {
    proxy_pass https://your-store.myshopify.com/;
    proxy_set_header Host your-store.myshopify.com;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_redirect off;
}

Setup and local testing before you go live

Bind the worker to the relevant route in the Cloudflare dashboard (example.com/shop* → worker) rather than editing DNS directly — the route, not the domain name record, is what triggers the proxy. Before pointing production traffic at it, use the Shopify CLI (or Zendesk's equivalent staging/preview environment) to confirm the proxied paths render correctly against a local development copy of the store. This catches broken asset paths and theme-specific redirects before they're visible to Googlebot rather than after.

What to check before calling it done

Once the worker is bound and tested locally, verify three things in production before treating the migration as complete:

  • Host header rewriting — confirm the third-party platform accepts the proxied request without returning a 403 or redirect loop
  • Canonical tags — pages served at example.com/shop/item-1 need self-referential canonicals, not ones pointing back at the myshopify.com origin
  • Asset routing — relative CSS/JS/image paths on the third-party platform often need their base URL updated to match the proxied subfolder path, or they'll 404

Troubleshooting

Most failures trace back to one of a handful of causes once the worker is live and serving real requests:

  • 403 on every proxied request — the platform is rejecting the rewritten Host header; some Shopify/Zendesk setups need the custom domain added in their own settings before they'll accept traffic under a different hostname, not just the worker-side header rewrite
  • Redirect loop between the root domain and the platform's own domain — usually the platform is issuing its own redirect back to the canonical myshopify.com or zendesk.com URL because it doesn't recognise the proxied host as an allowed custom domain
  • Mixed-content or broken CSS/JS — relative asset paths on the platform's theme are resolving against the origin's own domain rather than the proxied path; this is the same asset-routing issue as above, but worth checking browser console errors directly against the live URL to confirm
  • Webhooks silently failing — if the platform validates a request signature against the original Host header, rewriting it at the edge can break signature verification; exclude webhook paths from the proxy or forward the original Host separately
  • Content not updating after a theme or help-center change — check Cloudflare's cache rules on the proxied route; edge caching a dynamic storefront path is a common cause of stale content that looks like a deployment failure but isn't
Paul Lovell

Written by

Paul Lovell

International SEO Consultant & Founder of Always Evolving SEO. 15+ years running technical audits, log-file analysis, and root-cause fixes for multimillion-dollar businesses — speaker at SMX London, Search & Content Summit, and SEMrush events.