diff --git a/doc/files/package.json.md b/doc/files/package.json.md
index 6324caf64a517..19d4704d0752d 100644
--- a/doc/files/package.json.md
+++ b/doc/files/package.json.md
@@ -170,6 +170,16 @@ Both email and url are optional either way.
 
 npm also sets a top-level "maintainers" field with your npm user info.
 
+## support
+
+You can specify a URL for up-to-date information about ways to support
+development of your package:
+
+    { "support": "https://example.com/project/support" }
+
+Users can use the `npm support` subcommand to list the `support` URLs
+of all dependencies of the project, direct and indirect.
+
 ## files
 
 The optional `files` field is an array of file patterns that describes
diff --git a/lib/config/cmd-list.js b/lib/config/cmd-list.js
index fa4390fcdcba7..c11c8c30982b8 100644
--- a/lib/config/cmd-list.js
+++ b/lib/config/cmd-list.js
@@ -91,6 +91,7 @@ var cmdList = [
   'token',
   'profile',
   'audit',
+  'support',
   'org',
 
   'help',
diff --git a/lib/install.js b/lib/install.js
index 8cc6d16bdd169..52fe96c47f5ff 100644
--- a/lib/install.js
+++ b/lib/install.js
@@ -119,6 +119,7 @@ var unlock = locker.unlock
 var parseJSON = require('./utils/parse-json.js')
 var output = require('./utils/output.js')
 var saveMetrics = require('./utils/metrics.js').save
+var validSupportURL = require('./utils/valid-support-url')
 
 // install specific libraries
 var copyTree = require('./install/copy-tree.js')
@@ -802,6 +803,8 @@ Installer.prototype.printInstalledForHuman = function (diffs, auditResult) {
   var added = 0
   var updated = 0
   var moved = 0
+  // Check if any installed packages have support properties.
+  var haveSupportable = false
   // Count the number of contributors to packages added, tracking
   // contributors we've seen, so we can produce a running unique count.
   var contributors = new Set()
@@ -809,6 +812,11 @@ Installer.prototype.printInstalledForHuman = function (diffs, auditResult) {
     var mutation = action[0]
     var pkg = action[1]
     if (pkg.failed) return
+    if (
+      mutation !== 'remove' && validSupportURL(pkg.package.support)
+    ) {
+      haveSupportable = true
+    }
     if (mutation === 'remove') {
       ++removed
     } else if (mutation === 'move') {
@@ -872,7 +880,12 @@ Installer.prototype.printInstalledForHuman = function (diffs, auditResult) {
   report += ' in ' + ((Date.now() - this.started) / 1000) + 's'
 
   output(report)
-  return auditResult && audit.printInstallReport(auditResult)
+  if (haveSupportable) {
+    output('Run `npm support` to support projects you depend on.')
+  }
+  if (auditResult) {
+    audit.printInstallReport(auditResult)
+  }
 
   function packages (num) {
     return num + ' package' + (num > 1 ? 's' : '')
diff --git a/lib/support.js b/lib/support.js
new file mode 100644
index 0000000000000..5813df93ff2a6
--- /dev/null
+++ b/lib/support.js
@@ -0,0 +1,88 @@
+'use strict'
+
+const npm = require('./npm.js')
+const output = require('./utils/output.js')
+const path = require('path')
+const readPackageTree = require('read-package-tree')
+const semver = require('semver')
+const validSupportURL = require('./utils/valid-support-url')
+
+module.exports = support
+
+const usage = require('./utils/usage')
+support.usage = usage(
+  'support',
+  '\nnpm support [--json]'
+)
+
+support.completion = function (opts, cb) {
+  const argv = opts.conf.argv.remain
+  switch (argv[2]) {
+    case 'support':
+      return cb(null, [])
+    default:
+      return cb(new Error(argv[2] + ' not recognized'))
+  }
+}
+
+// Compare lib/ls.js.
+function support (args, silent, cb) {
+  if (typeof cb !== 'function') {
+    cb = silent
+    silent = false
+  }
+  const dir = path.resolve(npm.dir, '..')
+  readPackageTree(dir, function (err, tree) {
+    if (err) {
+      process.exitCode = 1
+      return cb(err)
+    }
+    const data = findPackages(tree)
+    if (silent) return cb(null, data)
+    var out
+    if (npm.config.get('json')) {
+      out = JSON.stringify(data, null, 2)
+    } else {
+      out = data.map(displayPackage).join('\n\n')
+    }
+    output(out)
+    cb(err, data)
+  })
+}
+
+function findPackages (root) {
+  const set = new Set()
+  iterate(root)
+  return Array.from(set).sort(function (a, b) {
+    const comparison = a.name
+      .toLowerCase()
+      .localeCompare(b.name.toLowerCase())
+    return comparison === 0
+      ? semver.compare(a.version, b.version)
+      : comparison
+  })
+
+  function iterate (node) {
+    node.children.forEach(recurse)
+  }
+
+  function recurse (node) {
+    const metadata = node.package
+    const support = metadata.support
+    if (support && validSupportURL(support)) {
+      set.add({
+        name: metadata.name,
+        version: metadata.version,
+        path: node.path,
+        homepage: metadata.homepage,
+        repository: metadata.repository,
+        support: metadata.support
+      })
+    }
+    if (node.children) iterate(node)
+  }
+}
+
+function displayPackage (entry) {
+  return entry.name + '@' + entry.version + ': ' + entry.support
+}
diff --git a/lib/utils/valid-support-url.js b/lib/utils/valid-support-url.js
new file mode 100644
index 0000000000000..d575dcdf03b52
--- /dev/null
+++ b/lib/utils/valid-support-url.js
@@ -0,0 +1,19 @@
+const URL = require('url').URL
+
+// Is the value of a `support` property of a `package.json` object
+// a valid URL for `npm support` to display?
+module.exports = function (argument) {
+  if (typeof argument !== 'string' || argument.length === 0) {
+    return false
+  }
+  try {
+    var parsed = new URL(argument)
+  } catch (error) {
+    return false
+  }
+  if (
+    parsed.protocol !== 'https:' &&
+    parsed.protocol !== 'http:'
+  ) return false
+  return parsed.host
+}
diff --git a/package-lock.json b/package-lock.json
index 37297b2f6bad4..58daf760c4245 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -438,6 +438,17 @@
             "has-ansi": "^2.0.0",
             "strip-ansi": "^3.0.0",
             "supports-color": "^2.0.0"
+          },
+          "dependencies": {
+            "has-ansi": {
+              "version": "2.0.0",
+              "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+              "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^2.0.0"
+              }
+            }
           }
         },
         "supports-color": {
@@ -2415,15 +2426,6 @@
         "function-bind": "^1.1.1"
       }
     },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
     "has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
diff --git a/test/tap/install-mention-support.js b/test/tap/install-mention-support.js
new file mode 100644
index 0000000000000..a1cb2c4aaf7d7
--- /dev/null
+++ b/test/tap/install-mention-support.js
@@ -0,0 +1,39 @@
+'use strict'
+var test = require('tap').test
+var Tacks = require('tacks')
+var Dir = Tacks.Dir
+var File = Tacks.File
+var common = require('../common-tap.js')
+
+var fixturepath = common.pkg
+var fixture = new Tacks(Dir({
+  'package.json': File({}),
+  'hassupport': Dir({
+    'package.json': File({
+      name: 'hassupport',
+      version: '7.7.7',
+      support: 'http://example.com/project/support'
+    })
+  })
+}))
+
+test('setup', function (t) {
+  fixture.remove(fixturepath)
+  fixture.create(fixturepath)
+  t.end()
+})
+
+test('install-report', function (t) {
+  common.npm(['install', '--no-save', './hassupport'], {cwd: fixturepath}, function (err, code, stdout, stderr) {
+    if (err) throw err
+    t.is(code, 0, 'installed successfully')
+    t.is(stderr, '', 'no warnings')
+    t.includes(stdout, '`npm support`', 'mentions `npm support`')
+    t.end()
+  })
+})
+
+test('cleanup', function (t) {
+  fixture.remove(fixturepath)
+  t.end()
+})
diff --git a/test/tap/support.js b/test/tap/support.js
new file mode 100644
index 0000000000000..93d4887423a13
--- /dev/null
+++ b/test/tap/support.js
@@ -0,0 +1,77 @@
+'use strict'
+var test = require('tap').test
+var Tacks = require('tacks')
+var path = require('path')
+var Dir = Tacks.Dir
+var File = Tacks.File
+var common = require('../common-tap.js')
+
+var fixturepath = common.pkg
+var fixture = new Tacks(Dir({
+  'package.json': File({
+    name: 'a',
+    version: '0.0.0',
+    dependencies: { 'hassupport': '7.7.7' }
+  }),
+  'node_modules': Dir({
+    hassupport: Dir({
+      'package.json': File({
+        name: 'hassupport',
+        version: '7.7.7',
+        homepage: 'http://example.com/project',
+        support: 'http://example.com/project/donate'
+      })
+    })
+  })
+}))
+
+test('setup', function (t) {
+  fixture.remove(fixturepath)
+  fixture.create(fixturepath)
+  t.end()
+})
+
+test('support --json', function (t) {
+  common.npm(['support', '--json'], {cwd: fixturepath}, function (err, code, stdout, stderr) {
+    if (err) throw err
+    t.is(code, 0, 'exited 0')
+    t.is(stderr, '', 'no warnings')
+    var parsed
+    t.doesNotThrow(function () {
+      parsed = JSON.parse(stdout)
+    }, 'valid JSON')
+    t.deepEqual(
+      parsed,
+      [
+        {
+          name: 'hassupport',
+          version: '7.7.7',
+          homepage: 'http://example.com/project',
+          support: 'http://example.com/project/donate',
+          path: path.resolve(fixturepath, 'node_modules', 'hassupport')
+        }
+      ],
+      'output data'
+    )
+    t.end()
+  })
+})
+
+test('support', function (t) {
+  common.npm(['support'], {cwd: fixturepath}, function (err, code, stdout, stderr) {
+    if (err) throw err
+    t.is(code, 0, 'exited 0')
+    t.is(stderr, '', 'no warnings')
+    t.includes(stdout, 'hassupport', 'outputs project name')
+    t.includes(stdout, '7.7.7', 'outputs project version')
+    t.includes(stdout, 'http://example.com/project', 'outputs contributor homepage')
+    t.includes(stdout, 'http://example.com/project/donate', 'outputs support link')
+    t.end()
+  })
+})
+
+test('cleanup', function (t) {
+  t.pass(fixturepath)
+  fixture.remove(fixturepath)
+  t.end()
+})