diff --git a/README.md b/README.md
index dd4c97e2e3c2b1596714531699c68ba387d3ac14..987046244b330b073e62ea661a7854ef727c677f 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,10 @@ You can read a [tutorial on how to add a badge][tutorial].
 4. Run `npm start` to start the server.
 5. Open `http://localhost:3000/` to view the frontend.
 
+To debug a badge from the command line, run `npm run badge -- /npm/v/nock.svg`.
+It also works with full URLs like
+`npm run badge -- https://img.shields.io/npm/v/nock.svg`.
+
 Shields has experimental support for [Gitpod Beta][gitpod], a pre-configured development
 environment that runs in your browser. To use Gitpod, click the button below and
 sign in with GitHub. Gitpod also offers a browser add-on, though it is not required.
diff --git a/package.json b/package.json
index 760c054a8022585263834a017077e55bfa44d173..5a42ccd2109038bcf6dad9ef71fa6e7c2590fa30 100644
--- a/package.json
+++ b/package.json
@@ -106,7 +106,8 @@
     "now-start": "node server",
     "prestart": "run-s --silent depcheck defs features",
     "start": "concurrently --names server,frontend \"npm run start:server\" \"cross-env BASE_URL=http://localhost:8080 BABEL_ENV=dev-prod next dev\"",
-    "refactoring-report": "node scripts/refactoring-cli.js"
+    "refactoring-report": "node scripts/refactoring-cli.js",
+    "badge": "cross-env NODE_CONFIG_ENV=test TRACE_SERVICES=true node scripts/badge-cli.js"
   },
   "lint-staged": {
     "**/*.js": [
diff --git a/scripts/badge-cli.js b/scripts/badge-cli.js
new file mode 100644
index 0000000000000000000000000000000000000000..3bf5da67f4684dd16a704be49b7318adbc321deb
--- /dev/null
+++ b/scripts/badge-cli.js
@@ -0,0 +1,47 @@
+'use strict'
+
+const { URL } = require('url')
+const got = require('got')
+const emojic = require('emojic')
+const Server = require('../core/server/server')
+const trace = require('../core/base-service/trace')
+
+const config = require('config').util.toObject()
+
+function normalizeBadgeUrl(url) {
+  // Provide a base URL in order to accept fragments.
+  const { pathname, searchParams } = new URL(url, 'http://example.com')
+  const newPath = pathname.replace('.svg', '.json')
+  searchParams.set('style', '_shields_test')
+  return `${newPath}?${searchParams.toString()}`
+}
+
+async function traceBadge(badgeUrl) {
+  const server = new Server(config)
+  await server.start()
+  const { body } = await got(
+    `${server.baseUrl.replace(/\/$/, '')}${badgeUrl}`,
+    { json: true }
+  )
+  trace.logTrace('outbound', emojic.shield, 'Rendered badge', body)
+  await server.stop()
+}
+
+async function main() {
+  if (process.argv.length < 3) {
+    console.error(`Usage: node ${__filename} badge-url`)
+    process.exit(1)
+  }
+  const [, , url] = process.argv
+  const normalized = normalizeBadgeUrl(url)
+  await traceBadge(normalized)
+}
+
+;(async () => {
+  try {
+    await main()
+  } catch (e) {
+    console.error(e)
+    process.exit(1)
+  }
+})()