About app security
Merchants trust Shopify with their business, and when they install your app, they extend that trust to you. Shopify provides tools to help you earn that trust, such as app templates and libraries that handle authentication, and app security checks that find Shopify-specific issues in your code.
Agentic development has changed the pace of security for both attackers and developers. AI agents find weaknesses in minutes that used to take weeks of manual review, and that capability is as available to attackers as it is to you. An app that was secure enough a year ago needs a stronger posture today.
The same tooling makes hardening your app faster than it has ever been. Our app security check and these guides help you (and your agent) implement our best practices across your apps.
Anchor to Vulnerabilities that attackers target mostVulnerabilities that attackers target most
Attackers look for the same few weaknesses across many apps at once, mostly ones that let them change what renders on a storefront or read and change another store's orders, customers, and settings. This list pulls those weaknesses from every section in Best practices by area, in priority order.
| # | Vulnerability | How to fix it |
|---|---|---|
| 1 | Endpoints that trust a shop parameter | Take the store from a verified ID token, HMAC, or app proxy signature. See Authenticate every request. |
| 2 | Unescaped or shared values in theme app extensions | Escape each value for its context, and render only settings that your app owns. See Escape every value for its context and Store storefront settings where only your app can write them. |
| 3 | Runtime configuration and internal tools that reach storefronts | Never run fetched configuration as code, and hold staff tools to the same standard as your app. See Treat runtime configuration as data and Hold internal tools to the same standard as your app. |
| 4 | Cross-shop data access (IDOR) | Look records up by the authenticated shop and the ID together. See Scope every query to the authenticated shop. |
| 5 | Unverified webhooks and app proxy requests | Verify the HMAC or signature before you trust any value in the request. See Authenticate every request. |
| 6 | Leaked or non-expiring credentials | Keep secrets out of bundles and logs, and use expiring offline access tokens. See Keep secrets out of shipped code and logs and Use expiring offline access tokens. |
| 7 | Stolen tokens used from an attacker's servers | Allowlist your servers' IP addresses, so a leaked token can make GraphQL and REST Admin API requests only from them. See Restrict API access to your IP addresses. |
| 8 | Compromised accounts around your app | Turn on multi-factor authentication everywhere, and remove access when people leave. See Protect the accounts that control your app. |
If your app stores custom CSS, JavaScript, or HTML, then also audit what's already stored.
Anchor to Security and the Shopify App StoreSecurity and the Shopify App Store
Shopify reviews every app's security before publishing it in the Shopify App Store. The review checks the security requirements for App Store review, such as TLS, closed network ports, and secure token generation, and the iframe protection that embedded apps need.
Anchor to Best practices by areaBest practices by area
Each area covers one part of your app. Each practice has its own page that explains what to do, why it matters, and how to do it. Many pages also explain how to verify it.
Anchor to App server and UIApp server and UI
Your app's server and the app that you embed in the Shopify admin. Shopify's app templates handle many of these practices for you. If you don't use an app template, then these practices are critical. If you do, then check your app against them anyway.
| Practice | Summary | Handled by the app template |
|---|---|---|
| Authenticate every request | Take the store from a verified ID token, app proxy signature, or webhook HMAC, never from a request parameter. | Yes, through the authenticate helpers. |
| Scope every query to the authenticated shop | Look records up by the authenticated shop and the ID together. | No. |
| Render untrusted content as text | Use textContent, not innerHTML. Sanitize HTML on the server with an allowlist, and validate URL schemes. | Partly. React escapes JSX text. dangerouslySetInnerHTML and server-rendered output are yours. |
| Prevent injection and request forgery | Bind SQL values, allowlist outbound hosts, authenticate state changes with a session token, redirect only to allowlisted URLs, and treat model output as untrusted. | Partly. Session-token authentication covers CSRF, and Prisma binds query values. SSRF, redirects, and model output are yours. |
| Handle uploads as untrusted input | Validate, rename, and re-encode uploads, store them outside your app, and reject SVG, XML, and HTML unless you truly need them. | No. |
| Validate cross-frame messages | Compare event.origin with === against an exact value, check event.source, and never post sensitive data to *. | Partly. addDocumentResponseHeaders sets the frame headers. postMessage handlers are yours. |
| Set up iframe protection | Send a frame-ancestors directive that lets only the authenticated shop's admin frame an embedded app, or 'none' for a standalone app. App Store review checks for it. | Yes, through addDocumentResponseHeaders. |
Anchor to Storefront and theme app extensionsStorefront and theme app extensions
Theme app extensions, app proxies, and anything else your app renders on a storefront. Every value your extension renders must be escaped for its context, come from a source only your app can change, and never run as code.
| Practice | Summary |
|---|---|
| Use theme app extensions instead of script tags (required) | Script tags are deprecated and load code that merchants can't see or turn off. Theme app extension assets are versioned and deployed with your app. |
| Escape every value for its context | escape for HTML, json inside <script>, url_encode in URLs. Validate custom CSS when it's saved, because no filter makes it safe. |
| Store storefront settings where only your app can write them | Other apps, staff, and translations can write to public namespaces. Use $app or app-data metafields, and migrate existing values. |
| Don't serve user-written code to storefronts | Offer structured settings instead of custom JavaScript, HTML, or CSS that your app supplies to storefronts at runtime. |
| Treat runtime configuration as data | Never run fetched configuration as code. Ship behavior in versioned extension assets, and fetch only data. |
| Declare your app's network access | List the hosts your app links to and the servers it loads from or sends requests to. We use the list to protect your app and monitor storefronts for malicious requests. |
Anchor to Credentials and dataCredentials and data
Your app's credentials, store access, and merchant and customer data. Shopify limits what a leaked credential can do, and these practices make sure your app takes advantage of those limits and holds as little as it needs to.
| Practice | Summary |
|---|---|
| Use expiring offline access tokens (required) | A non-expiring token stays valid until uninstall or secret rotation. An expiring one lasts one hour. Required for public apps from January 1, 2027. |
| Restrict API access to your IP addresses | A stolen token can call the GraphQL and REST Admin API from anywhere unless you allowlist the addresses your servers use. Shopify rejects requests from outside them with a 403. |
| Request only the access scopes that you use | Use optional scopes for features that only some stores use, and revoke scopes you no longer need. |
| Keep secrets out of shipped code and logs | Bundles, theme assets, source maps, and logs are readable by others. Automated scanners search them for credentials. |
| Collect only the customer data that you need | Data you don't hold can't leak. Never capture login fields, DOM snapshots, or payment data. |
Anchor to Infrastructure and accountsInfrastructure and accounts
The infrastructure around your app's code, including your servers, internal tools, and dependencies.
| Practice | Summary |
|---|---|
| Expose only a hardened web server | One audited ingress on the internet. Databases, queues, admin panels, and staging stay private. TLS everywhere. |
| Hold internal tools to the same standard as your app | Internal tools are discoverable. Stored XSS in a staff tool becomes a cross-store attack. |
| Protect the accounts that control your app | Multi-factor authentication on Partner, hosting, DNS, source control, registry, and email accounts. Remove access when people leave. |
| Keep dependencies and runtimes current | Fewer, pinned, reviewed dependencies. Patch runtimes and rebuild base images on a schedule. Pin CDN scripts with Subresource Integrity. |
Anchor to Monitoring and incident responseMonitoring and incident response
These practices help you maintain a strong security posture through your processes and automation.
| Practice | Summary |
|---|---|
| Check your app on every release | The checks find Shopify-specific issues in minutes. Scan every codebase that touches merchant data, not only the app directory. |
| Review your app's API logs | A leaked token shows up as API traffic from somewhere other than your servers. Filter by client IP and user agent. |
| Respond to a security incident | Contain, revoke, preserve evidence, notify Shopify, scope the impact, fix the root cause, meet your obligations, write it up. |
| Audit stored settings and custom code | Scan stored CSS, JavaScript, and HTML for script elements, unknown domains, eval and atob, and code that touches checkout or login. |
Anchor to App security checksApp security checks
The app security checks scan your app's source code for Shopify-specific issues, such as a route that doesn't authenticate requests or a database query that isn't scoped to one shop. Run them before you submit your app and on every release.
The app security checks don't cover every best practice in these docs. They can't check configuration outside your code, such as your hosting and accounts, or your team's processes, such as reviewing your API logs and responding to incidents. They also scan only the directories that you point them at. To include a separate backend or an internal tool, see Check your app on every release.
With the AI Toolkit installed, ask your coding agent to run them: