# vinkla/hashids: obfuscating database ids in PHP without encryption

> A small PHP library that turns numeric ids into short, YouTube-like strings. It is not encryption, it needs bcmath or gmp, and the README says so more plainly than most projects do.

**vinkla/hashids** — A small PHP library to generate YouTube-like ids from numbers. Use it when you don't want to expose your database ids to the user.

- Repository: https://github.com/vinkla/hashids
- Website: https://hashids.org/php
- Stars: 5,428 · Forks: 412
- Language: PHP
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/vinkla-hashids

## The problem vinkla/hashids solves, and for whom

Sequential primary keys leak information. A URL ending in /invoices/1043 tells the reader roughly how many invoices exist, roughly when the record was created relative to its neighbours, and gives them a trivially enumerable handle. The usual fixes are UUIDs (long, and often awkward as a primary key), separate public slugs (extra column, extra uniqueness constraint), or encryption (real cryptography, real key management). vinkla/hashids sits in a fourth slot: a reversible, keyed transformation from integers to short alphanumeric strings.

The library is for PHP developers who already have numeric ids and want to stop exposing them, without changing the schema. The README frames it as "a small PHP library to generate YouTube-like ids from numbers" and says to use it "when you don't want to expose your database numeric ids to users". That is a narrow, honest scope. It is not a general-purpose id generator and it is not a security control, and the README repeats that point in its own pitfalls list: "Do not use this library as a security measure. Do not encode sensitive data with it. Hashids is not an encryption library."

## How the encode and decode round trip actually works

You construct a Hashids object with three optional arguments: a salt (the README calls it a project name), a minimum output length, and a custom alphabet. The same salt produces the same output for the same input, so the mapping is deterministic and reversible. The README shows the salt changing the result: with no salt, encode(1, 2, 3) returns o2fXhV; with the salt 'My Project' it returns Z4UrtW, and with 'My Other Project' it returns gPUasb.

The minimum length argument pads output rather than fixing it. The README is explicit: "output ids are only padded to fit at least a certain length. It doesn't mean that they will be exactly that length." With no padding, encode(1) gives jR; constructed as new Hashids('', 10), the same call gives VolejRejNm. If you were planning to enforce a fixed-width public id in a database column or a URL regex, that padding rule will surprise you.

The number of arguments is not preserved in the encoded string in the way you might assume. The README shows encode(1, 2, 3) producing o2fXhV, and encode(1) producing jR, so a single number and a tuple of numbers are different inputs. But decode always returns an array, even for a single value: decode(encode(1)) gives [1], not 1. Callers that expect a scalar will need to index into the result.

The alphabet argument is the third lever. Passing 'abcdefghijklmnopqrstuvwxyz' as the third constructor argument produces all-lowercase output, and the README's example gives mdfphx for encode(1, 2, 3). That is useful when a downstream system is case-insensitive, but it also reduces the character space per position, so ids get longer for the same input.

There is a separate path for hexadecimal input, encodeHex and decodeHex, which the README suggests for MongoDB ObjectIds. It states there is no limit on how large a hex number you can pass, and it does not have to be a Mongo ObjectId. That makes the library usable for hex-shaped identifiers that are not integers at all.

## Installing vinkla/hashids with Composer and encoding your first id

The README gives one install command. Run it in the root directory of your project:

```bash
composer require hashids/hashids
```

Composer will resolve the package from Packagist and add it to composer.json. The README does not document a version constraint, so if you need to pin to the 5.x line you will have to write that constraint yourself rather than copying one from the documentation.

Before any of this runs, check your PHP extensions. The README states that Hashids requires either the bcmath or the gmp extension. On a stock PHP build one of them is often missing, and the failure appears at runtime rather than at install time, so it is worth confirming the extension is loaded in every environment including CI and your production image.

The smallest working example is three lines. Import the class, construct it, encode a number:

```php
use Hashids\Hashids;

$hashids = new Hashids();

$hashids->encode(1);
```

The README's quick example goes one step further and shows the round trip, which is the part worth running before you build anything on top of it:

```php
use Hashids\Hashids;

$hashids = new Hashids();

$id = $hashids->encode(1, 2, 3); // o2fXhV
$numbers = $hashids->decode($id); // [1, 2, 3]
```

If you see o2fXhV and then [1, 2, 3], the library is working and you have a salt-free instance. For anything real you want a salt, because the default instance is the one every other user of this library also has. Passing a project name changes the output, as the README's 'My Project' example shows, and that salt is what stops your ids from being interchangeable with everyone else's.

## Where vinkla/hashids breaks down

The salt is not a secret key in any cryptographic sense. Anyone who knows your salt and your alphabet can decode any id you produce, and the algorithm is public. If your threat model includes an attacker who can guess or obtain the salt, the obfuscation buys you nothing. The README does not claim otherwise, but the framing as "obfuscation" is easy to read past.

Negative numbers are not supported. The README lists this as a pitfall with no workaround offered, so any id column that can hold a negative value, or any offset scheme that produces one, is out of scope.

Bogus input to encode returns an empty string rather than throwing. The README's example is encode('123a') and the assertion that the result is strictly equal to ''. That means a typo or a bad cast silently produces an empty id, which then travels into a URL. You need to check for the empty string yourself, because the library will not raise anything.

Decode always returns an array, as noted above, and there is no documented exception for malformed input either. The README does not describe what decode does with a string that was never produced by the same instance, which is a gap worth testing yourself before you rely on it for input validation.

The padding rule is the other trap. Because ids are padded to at least a length rather than exactly that length, you cannot use a fixed-length column or a strict URL pattern as a validation shortcut. A pattern that accepts the padded form will also accept shorter genuine ids.

Finally, the same number encodes to a different string depending on the salt and alphabet, so ids are not portable across environments unless every environment shares the same constructor arguments. Rotating a salt invalidates every previously issued id, and the README does not document a migration path for that.

## Hashids vs Sqids, and what the README says about the successor

The README carries a note that the creator of Hashids has released a new, upgraded version rebranded as Sqids, and links to the sqids-php repository and a comparison page on the Sqids site. The note also states that Hashids will continue to be maintained and available for future use. That is the project's own position, and it is the most useful piece of guidance in the README for anyone choosing between the two.

The difference in approach is not spelled out in the available documentation beyond the word "upgraded" and the link to a comparison page. What the README does establish is that the two are separate packages with separate repositories, so adopting Hashids today is a deliberate choice to stay on the older algorithm rather than a default.

For a PHP project already using hashids/hashids, the practical question is whether the existing ids need to keep decoding. Since ids are deterministic functions of the salt, alphabet and input, moving to a different algorithm means every previously issued id stops resolving unless you keep the old library around to read it. The README does not describe a dual-read strategy, and the CHANGELOG is the file to check for what changed between 5.0.0 and 5.0.2.

## Maintenance, licence and the cost of upgrading

The repository is not archived. The last push was on 2026-04-16. The most recent tagged release listed is 5.0.2, published on 2023-02-23, with 5.0.1 and 5.0.0 both landing in February 2023. So there is a gap between repository activity and tagged releases, and anyone who pins to a version number should be aware that the newest tag is not recent.

The licence is MIT. In practical terms that permits use, modification and redistribution provided the copyright notice and permission notice are retained, and it comes with no warranty. This is not legal advice; if your organisation has a policy on permissively licensed dependencies, MIT is the category to check against it.

The upgrade cost is dominated by the salt and alphabet, not by the API. The constructor signature takes the salt, the minimum length and the alphabet, and the README does not describe a way to re-key existing ids. A major version bump that changed the algorithm would therefore strand every id already in the wild unless the old instance is kept for decoding. The CHANGELOG.md file at the repository root is where the project records what actually changed across 5.0.0, 5.0.1 and 5.0.2, and it is the first file to read before bumping the constraint in composer.json.

## Conclusion

Adopt vinkla/hashids when you want short, URL-safe strings in place of sequential numeric ids and you accept that the mapping is reversible by anyone who knows the algorithm. Do not adopt it for tokens, password resets, signed URLs or anything where an attacker guessing the plaintext matters: the README states it is not an encryption library and that sensitive data should not be encoded with it. Before you commit, verify that bcmath or gmp is enabled in every environment you deploy to, because the README requires one of the two, and check the last push date against your own tolerance for a library whose latest tagged release is 5.0.2 from 2023-02-23.

## FAQ

### What is vinkla/hashids used for?

It encodes numeric ids into short, YouTube-like strings so that sequential database ids do not appear in URLs or other user-visible places. The README frames it as obfuscation rather than security and states plainly that it is not an encryption library.

### How do I install vinkla/hashids?

Run composer require hashids/hashids in the root directory of your project. The README notes that the library requires either the bcmath or the gmp PHP extension to work.

### Is vinkla/hashids the same as Sqids?

No. The README states that the creator of Hashids released a new, upgraded version rebranded as Sqids, hosted in a separate repository, and links to a comparison page on the Sqids site. The same note says Hashids will continue to be maintained and available for future use.

### What are alternatives to vinkla/hashids?

The alternative the project itself points to is Sqids, which the README describes as an upgraded version by the same creator and links to a comparison page for. Because ids are deterministic given the salt and alphabet, switching means previously issued ids no longer decode unless the old library is kept for reading them.

## Sources

- [License: MIT](https://github.com/vinkla/hashids/blob/master/LICENSE)
- [Project website](https://hashids.org/php)
- [README](https://github.com/vinkla/hashids/blob/master/README.md)
- [Releases](https://github.com/vinkla/hashids/releases)
- [vinkla/hashids on GitHub](https://github.com/vinkla/hashids)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/vinkla-hashids
