Skip to content
Ecom

How to Decommission a Shopify App Without Breaking Anything

Uninstalling a Shopify app removes the access token, not the debris. The veteran guide to theme residue, orphan metafields, webhook ghosts, and GDPR endpoints the store owner inherits.

By WitsCode10 min read

Click the trash icon on an app row and Shopify tells you the app is gone. It is not gone. What Shopify actually did is revoke the OAuth access token and queue a shop/redact webhook for 48 hours later. Everything the app wrote into your store over the months or years it was installed is still there: theme edits, metafield definitions on every product, script tags served to every pageview, webhook subscriptions the app registered under a custom token, checkout extensions pinned to old versions, web pixels still firing, files sitting in the Files API, customer tags feeding stale email segments, and Flow actions broken mid-workflow. The Shopify help article does not mention any of this, and the dozen SEO pieces that rank for "uninstall Shopify app" all stop at "remove the code from your theme." That advice would be fine in 2017. In 2026, with app-block architecture, metaobjects, Functions, Pixels, and checkout extensions, a Shopify app is a multi-surface citizen and uninstalling it only pulls the passport. This is the veteran guide to the four-area debris audit, the exact GraphQL queries to surface orphans, and the decision framework for what is safe to delete versus what needs to be migrated first.

What "uninstall" actually does, and what it leaves behind

When a merchant hits Delete on an installed app, Shopify performs three mechanical actions. It invalidates the access token tied to that installation. It removes webhook subscriptions registered against that specific token. It schedules the shop/redact GDPR webhook to fire in 48 hours so the app vendor can purge shop data from their systems. That is the entire cleanup Shopify guarantees.

What Shopify does not touch: any data the app wrote to your store objects. Metafields, metaobjects, tags, files, discounts, script tags registered via the Asset API rather than the ScriptTag resource, theme edits made through the Online Store API, app blocks saved into settings_data.json, Shopify Flow workflow steps, web pixels, checkout UI extension pins, Functions ownership records, and app proxy route entries in navigation menus or transactional emails. None of it. The merchant inherits all of it as unowned data the moment the app is uninstalled. Two years of accumulating installs and uninstalls produces what I have come to think of as app sediment: a thin layer of residue at every layer of the store, each piece of which loads on every page, runs on every order, or silently breaks on the next theme update.

The uninstall-as-eviction model works only if you treat the four areas below as a required hygiene pass rather than optional tidying.

Area one: theme files and store-level scripts

Start with the theme, because this is what every SEO post tells you to do, and they all do it wrong. The common advice is "open theme.liquid and remove the app's code block." That catches about a third of the residue.

Pull the live theme locally using Shopify CLI (shopify theme pull --live) and diff it against your last clean commit. If you do not have a clean commit, diff against a freshly duplicated Dawn or your vendor theme at the same version. The diff will surface edits you forgot an app made. Look for commented sentinels like <!-- BEGIN app_name --> and <!-- END app_name --> in layout/theme.liquid, raw <script> tags pointing to cdn.appdomain.com, CSS link tags, and Liquid {% render 'app-snippet' %} or {% include 'klaviyo' %} calls. Walk the snippets/ folder looking for files named after apps (common offenders: judgeme_*, loox-*, pagefly-*, shogun-*, yotpo_*, klaviyo.liquid). For each snippet, ripgrep the theme for its name. Unreferenced snippets are safe to delete.

The config/settings_data.json file holds app block references using the shopify://apps/<handle>/blocks/<block>/<uuid> URI. When an app is uninstalled, these block entries remain as ghost entries. The theme editor hides them but they stay in the JSON until something overwrites the file. Search settings_data.json for shopify://apps/ and delete entries whose app you have removed.

Then look at store-level scripts, which are not in the theme at all. Legacy apps and some current ones still use the ScriptTag resource to inject JavaScript that runs on every storefront pageview. These survive uninstall in some edge cases, particularly when the app was installed before 2023 or when the merchant migrated stores. Query them:

{ scriptTags(first: 50) { edges { node { id src displayScope } } } }

Anything pointing to a shut-down vendor CDN or returning 404 should be deleted via the scriptTagDelete mutation. The assets/ folder often holds orphan JS and CSS files for the same apps: loox-widget.js, pagefly-editor.css, pixel scripts dropped by analytics apps. If nothing in the theme references them, they are dead weight loaded every time someone hits the site.

Finally, check Settings -> Customer Events for custom web pixels. These are installed by many analytics and affiliate apps and they are not theme files, not script tags, and not removed on uninstall. They run on every page and every order. Disable and delete any pixel whose parent app is gone.

Area two: metafield namespaces and metaobjects

This is the area the SERP completely misses, and it is the largest slice of residue by volume. Every serious Shopify app writes data into metafields using a namespace it owns. Judge.me writes to judgeme, Loox to loox, Yotpo to yotpo_reviews, Smartrr to smartrr, Shogun to shogun, Recharge to subscriptions, and so on. A mature store that has cycled through five review apps and two subscription apps can end up with eight orphan namespaces, each with two to six fields, attached to every product and every customer.

These metafield definitions count toward the 250-definition-per-owner-type limit. They show up as columns in every CSV export, confusing merchandisers who wonder why the "loox_rating" column is empty on new products. They appear in Flow condition builders as available variables. They show up as options in Search and Discovery filters. And if they contain JSON or reference types, they can cause Liquid rendering errors on the storefront.

To audit, run the metafield definitions query for each owner type. GraphQL:

query ProductMeta {
  metafieldDefinitions(first: 250, ownerType: PRODUCT) {
    edges { node { namespace key name type { name } } }
  }
}

Repeat for PRODUCTVARIANT, CUSTOMER, ORDER, COLLECTION, SHOP, COMPANY, and LOCATION. Each query returns the full list, and the namespace field tells you which app owns each one. Cross-reference against your installed apps. Any namespace that does not match a currently installed app is orphan debris.

Do not delete immediately. First, check whether the theme reads it. Ripgrep the pulled theme for metafields.<namespace> across all Liquid files. If you find product.metafields.loox.num_reviews in a snippet and Loox was uninstalled, the storefront is looking up a field the orphan definition still technically carries, and deleting the definition will wipe the values. The safe sequence is: migrate the values you want to keep to a custom.* namespace using a bulk operation, update Liquid references, verify the theme renders correctly in a preview, then delete the orphan namespace.

Also pull metaobjects via metaobjectDefinitions(first: 50). Page builders and content apps create metaobject definitions to store custom content types. Orphan metaobject definitions show up as mysterious content types in the Content area of admin and confuse anyone trying to manage custom content.

Area three: webhook subscriptions and app proxies

Shopify does revoke webhooks registered under an uninstalled app's access token. The residue comes from webhooks registered through a different channel: a custom app the merchant installed to connect a headless middleware, a Shopify Flow HTTP action pointed at the now-dead app's endpoint, or a webhook subscription created by a previous vendor using an admin API access token rather than the OAuth token. These survive uninstall because Shopify does not know they belonged to the same vendor.

List them:

{ webhookSubscriptions(first: 100) {
    edges { node {
      id topic
      endpoint { __typename
        ... on WebhookHttpEndpoint { callbackUrl }
      }
    } }
  }
}

Look for callback URLs pointing to vendor domains you no longer use. These webhooks fire on every relevant event (orders/create, products/update, and so on) and the receiving server either 404s or silently accepts and discards the payload. Each firing consumes a small slice of your API rate budget and, for orders, adds measurable latency if the webhook is synchronous to another action. Delete them with webhookSubscriptionDelete.

App proxies are a subtler residue. If the uninstalled app registered an app proxy at /apps/<prefix>, Shopify unregisters it automatically. What does not get cleaned up is every place in your store that linked to that proxy: navigation menus, footer links, policy pages, email templates, blog posts, CMS content. After uninstall the proxy URL 404s. Search your store's published content for /apps/ URLs and fix or remove them. Check transactional email templates specifically; they are easy to forget.

Area four: GDPR endpoints, extensions, Functions, and Flow

The fourth area is everything else Shopify treats as an attachable surface, each of which has its own lifecycle around uninstall.

GDPR mandatory webhooks (customers/data_request, customers/redact, shop/redact) are automatically cleaned up when the app is deleted, with the caveat that shop/redact fires 48 hours after uninstall. If the vendor's endpoint is still alive, they receive one final notice with the shop ID. This is not debris so much as a fact to know: if you are uninstalling because of a security incident or vendor compromise, assume the vendor still gets one more ping unless you route DNS for their callback to a sinkhole before the 48 hours elapses.

Checkout UI extensions deployed by the uninstalled app show up under Settings -> Checkout -> Configuration. After uninstall they render as "1 extension no longer available" badges on your checkout profile. They do not break checkout, but they clutter the configuration and confuse anyone reviewing checkout. Remove them from the profile.

Shopify Functions (cart transform, discount, delivery customization, payment customization) owned by the uninstalled app remain listed in the admin, marked inactive. They do not execute, but they take up slots against your Functions limits. Delete any whose parent app is gone.

Shopify Flow workflows that referenced the app's triggers or actions break at the affected step. Open Flow, filter by workflows with errors, and either replace the step with a native equivalent or disable the workflow entirely. Leaving broken Flow workflows enabled is how merchants discover six months later that their "tag VIP customers on second order" automation stopped working the day they uninstalled the app that provided the tagging action.

Files uploaded via the Files API by page builders, review apps, and content apps stay in the Files library. Storage is not billed, but the clutter makes finding legitimate files harder. Sort Files by date, filter to the install window of the uninstalled app, and bulk delete the orphans after verifying no current theme or page references them.

Customer, product, and order tags written by the app persist forever. Segments, Flow conditions, and customer lists built on those tags go stale silently. Audit Settings -> Customer segments for any segment whose rules include tags that were only being written by the uninstalled app, and either prune the rule or replace the tag source.

The decision framework: safe to delete versus migrate first

Not every piece of orphan data should be deleted. The framework I use on WitsCode engagements has three questions per artifact. First, is the data still read by anything in the store today? That means Liquid references for metafields, segment rules for tags, Flow conditions for anything. Second, is the data exported regularly to a downstream system like an ESP, CDP, or BI tool that still expects it? Third, does the merchant plan to reinstall a similar app within 60 days?

If all three answers are no, delete. If any answer is yes, migrate before deleting. For metafields that means copying values to a neutral custom.* namespace, updating every consumer, verifying in preview, and only then removing the orphan namespace. For tags it means writing a bulk operation that reapplies the tag under a neutral name before removing the original. For files it means re-uploading to the Files API under an owned asset name before removing the vendor-installed version. The extra work is worth it because the alternative, a merchant discovering a broken storefront on a Monday morning three weeks after the cleanup, is worse.

Why this compounds, and what to measure

A typical mid-sized Shopify store that is three years old has cycled through six to ten apps. Assume four of those were uninstalled. Conservatively that leaves forty to a hundred orphan metafield definitions across owner types, five to fifteen orphan script tags or asset files loaded on every page, two to six dead webhook subscriptions firing on common events, a handful of broken Flow steps, one or two orphan checkout extensions, and tens of megabytes of orphan files. I have measured stores where cleaning this residue dropped LCP by 400 to 900 milliseconds and cut JavaScript parse time on mobile by 30 to 40 percent. The checkout extension cleanup alone, on stores with three or more orphans, has cut checkout render time by 100 to 200 milliseconds in testing.

Measure before and after. Capture Lighthouse or PageSpeed scores for the homepage, a collection page, a product page, and the cart on desktop and mobile before you start. Capture the metafield definition count per owner type. Capture the webhook subscription count. Capture the script tag count. Do the cleanup, then capture again. Publish the diff to whoever signs your invoice.

Moving forward

If you are at the stage of actively uninstalling an app, do the four-area audit the same day. Residue is cheapest to clean when you still remember why the app was installed and which fields were used where. Waiting six months means reverse-engineering someone else's integration decisions from a dead vendor's scraped documentation.

WitsCode runs post-uninstall hygiene as a fixed-scope service. We pull the live theme, diff it, run the GraphQL orphan queries across every metafield owner type, enumerate script tags, pixels, webhooks, Functions, and checkout extensions, and produce a signed-off safe-delete plan with migrations identified for the keepers. Before and after PageSpeed screenshots come with the report. If your store has accumulated a few years of app debris and the storefront feels heavier than it should, that is usually why.

Get weekly field notes.

Practical writing on shipping products, straight to your inbox. No spam.

Need help with this?

Shopify Development

We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.

Talk to us

Want to discuss ecom for your business?

Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.