Safeguard
Open Source

shortid Is Deprecated: Why It Is Unsafe for IDs and What to Use Instead

The shortid npm package is deprecated by its own maintainers because the architecture is unsafe. Here is what is actually wrong with it and how to migrate to nanoid without breaking existing IDs.

Safeguard Research Team
Research
6 min read

The shortid npm package is deprecated, and not for the usual "maintainer moved on" reason: the project's own notice says the architecture is unsafe and points users at nanoid instead. That is unusually honest, and worth taking literally. shortid still gets substantial weekly downloads because it is baked into old tutorials, scaffolds, and long-lived codebases — every one of which is generating identifiers with weaker guarantees than the developer who wrote shortid.generate() believed they were getting. Here is what is actually wrong, how bad it is for your specific usage, and the migration path.

What shortid promised

shortid generated short, URL-friendly, "non-sequential" identifiers — 7 to 14 characters instead of a 36-character UUID. That pitch landed because UUIDs are genuinely awkward in URLs, and in the mid-2010s there was no small, obvious alternative. So shortid ended up naming database rows, share links, upload filenames, room codes, and — against its own documentation — occasionally password-reset and invite tokens.

The problem is the gap between "looks random" and "is random."

Why the architecture is unsafe

Two design decisions do the damage.

The randomness is decorative. shortid's output is substantially derived from state — time-based components and a worker/cluster counter — passed through a shuffled alphabet. The shuffle is driven by a seed, and it is a deterministic substitution: given the seed, the mapping from internal state to output characters is fixed. A substitution cipher over predictable state is not entropy, it is obfuscation. An observer who collects some generated IDs can reason about the generator's state and narrow the space of past and future IDs dramatically — the exact property unique IDs are supposed to deny.

It carried vulnerable internals. Later shortid versions delegated to an old copy of nanoid with known advisories, so even the genuinely random portion of its behavior inherited someone else's patched bugs. Depending on a deprecated wrapper around an outdated copy of its own replacement is as pure a case of dead-weight dependency risk as npm offers.

Neither issue produces a dramatic crash. Predictable-ID weaknesses surface as enumeration: a scraper walking your "unguessable" share links, a tester iterating upload URLs, an attacker who received one invite code inferring the shape of others. If IDs gate anything — even soft-privacy features like unlisted documents — predictability converts directly into unauthorized access. This is the same failure class as sequential integer IDs, merely with better camouflage.

How bad is it for you? A quick triage

Not every shortid usage is an incident. Rank yours:

  1. Security tokens (reset links, invites, API keys, session values): worst case. Replace immediately with a cryptographically random generator and invalidate outstanding tokens on a schedule.
  2. Unlisted-content URLs (share links, uploads): high. The ID is the access control; enumeration is the attack. Migrate new IDs now; consider regenerating links for sensitive existing content.
  3. Internal primary keys never exposed to users: low urgency security-wise, but the deprecated dependency still sits in your tree failing audits — schedule the swap.
  4. Client-side keys (React list keys, DOM ids): effectively zero risk. Replace at leisure to clear the deprecation warning.

Finding usage is straightforward:

npm ls shortid
grep -rn "shortid" src/

Remember the transitive case — older form libraries and CMS plugins bundled it. If a dependency you cannot patch pulls it in for non-security purposes, note it and move on; if it uses shortid for anything token-shaped, that dependency has a problem worth an upstream issue. Flagging deprecated packages before they land is one of the quieter wins of continuous dependency scanning — Safeguard, for instance, reports deprecation and known-bad architecture advisories alongside CVEs, so a shortid in a new lockfile never reaches main silently.

The replacement: nanoid

nanoid is the successor the shortid deprecation notice itself recommends, and it fixes both flaws by construction: it draws from the platform's cryptographic random source (Web Crypto / Node's crypto) rather than shuffled state, and it produces fixed-length IDs (21 characters by default) from a URL-safe alphabet, with strong collision resistance and a bundle size measured in bytes.

import { nanoid, customAlphabet } from "nanoid";

nanoid();        // "V1StGXR8_Z5jdHi6B-myT"  (21 chars, crypto-random)
nanoid(10);      // shorter, with proportionally reduced collision margin

// Need a specific alphabet or length? Stay crypto-random:
const roomCode = customAlphabet("23456789ABCDEFGHJKMNPQRSTUVWXYZ", 8);
roomCode();      // "7GKM2XQP"

Two migration notes that save a support ticket later:

  • Do not rewrite existing IDs. Old shortid values already in your database remain valid strings; keep validating both formats during the transition (shortid output is variable-length, nanoid default is 21 chars — a simple regex per format works).
  • Resist the urge to shrink length. nanoid(8) looks tidier but collision and guessing margins scale with length and alphabet. For anything security-adjacent, keep 21 characters or do the math explicitly with a collision calculator before shortening.

For true secrets (reset tokens, API keys), skip ID libraries entirely and use crypto.randomBytes(32) with appropriate encoding — that is a secret, not an identifier, and deserves full entropy. Our npm security best practices post covers the broader habit this incident teaches: read deprecation notices as security signals, not noise.

What this episode says about npm hygiene

shortid is the friendly version of a recurring npm story: a package everyone installed on reputation, whose guarantees nobody re-verified, quietly declared unsafe by its own author — while downloads continued barely dented. Deprecation on npm produces a warning string in install output and nothing else. No build fails, no dashboard turns red on its own. The teams that caught this early were the ones whose CI treats deprecated production dependencies as failures and whose review process asks "what generates our identifiers?" as a standing question. The teams that caught it late found out from a pentest report with a working enumeration script attached.

FAQ

Why exactly was shortid deprecated?

The maintainers deprecated it because its architecture is unsafe: output derives from predictable state passed through a deterministic seeded alphabet shuffle — a substitution over guessable input, not real entropy — and later versions also wrapped an outdated, vulnerable copy of nanoid. The official recommendation is to use nanoid directly.

Is shortid safe if I only use it for React keys or internal IDs?

For list keys and never-exposed internal identifiers, the predictability does not create a practical attack. You should still replace it eventually — it is unmaintained and trips dependency audits — but it is a housekeeping task, not an incident.

Do I need to regenerate IDs already created with shortid?

Usually no. Existing IDs keep working as opaque strings. Regenerate only where the ID itself is the access control for sensitive content (secret share links, tokens), and invalidate anything token-like on your normal rotation schedule.

Is nanoid actually secure enough for share links?

Yes — default 21-character nanoid uses a cryptographically secure random source and a 64-character alphabet, giving a keyspace that makes enumeration computationally irrelevant. For dedicated secrets like password-reset tokens, prefer crypto.randomBytes directly as a matter of principle.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.