PHPackages                             dsentker/url-fingerprint - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. dsentker/url-fingerprint

ActiveLibrary[HTTP &amp; Networking](/categories/http)

dsentker/url-fingerprint
========================

Build a hash from a url

0.4.0(4y ago)2325[1 PRs](https://github.com/dsentker/url-fingerprint/pulls)GPL-2.0-or-laterPHPPHP &gt;=7.4CI passing

Since Jul 16Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/dsentker/url-fingerprint)[ Packagist](https://packagist.org/packages/dsentker/url-fingerprint)[ Docs](https://github.com/dsentker/urlhasher)[ RSS](/packages/dsentker-url-fingerprint/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (4)Versions (8)Used By (0)

UrlFingerprint
==============

[](#urlfingerprint)

**A library that creates a unique hash value from a URL - without getting a headache.**

This library aims to get a unique but consistent *digest* (hash result) from a URL with or without side effects. It generates equal hash results even if the parameter order in the query string is different or the URL contains encoded characters like `%20`.

This library uses the concept of a "Fingerprint". Depending on your settings, a specific fingerprint is generated for each URL, which contains the digest, the hash algorithm and the hashed parts of the URL.

Basic Usage
-----------

[](#basic-usage)

```
$reader = new FingerprintReader([
    'secret' => 's3cre7v4lu3',
]);
$fingerprint1 = $reader->capture('http://www.example.com/info?id=42&details');

// Same URL, but different parameter order in query string.
$fingerprint2 = $reader->capture('http://www.example.com/info?details&id=42');

$reader->compare($fingerprint1, $fingerprint2); // bool(true)

echo $fingerprint1->digest; // d7335d0a237f47a049415a780c4e1c96
echo $fingerprint2->digest; // d7335d0a237f47a049415a780c4e1c96 - the same
```

Installation &amp; Usage
------------------------

[](#installation--usage)

[Composer 2](https://getcomposer.org/2) and PHP &gt;= 7.4 is required.

Install url-fingerprint with composer.

```
composer require dsentker/url-fingerprint
```

```
# Create a FingerprintReader instance and provide options in the
# constructor (the `secret` parameter is required).
$reader = new \UrlFingerprint\FingerprintReader([
    'secret' => '42',
    'hash_algo' => 'md5'
]);

# Capture a new fingerprint by given URL
$fingerprint = $reader->capture('http://www.github.com/');

echo $fingerprint->digest; // the result from the hash process
echo $fingerprint->hashAlgo; // md5
echo $fingerprint->gist; // a JSON representation of the url parts

# Use the compare method to test against another fingerprint
$reader->compare($fingerprint, $reader->capture('http://github.com'));
```

### Options

[](#options)

Configure options in the constructor to specify the way the digest is created:

OptionTypeDefaultDescription**`secret`**string**(required!)** Choose a secret key for the `hash_hmac`function.**`hash_algo`**stringsha256A [hashing algorithm suitable for `hash_hmac()`](https://www.php.net/manual/de/function.hash-hmac-algos.php).**`ignore_scheme`**booleanfalseWhether to hash the scheme part of the url (https://, ftp:// etc.)**`ignore_userinfo`**booleanfalseWhether to hash the [user information](https://www.ietf.org/rfc/rfc2396.txt) part of the url (e.g. userinfo@host)**`ignore_host`**booleanfalseWhether to hash the host name or not**`ignore_port`**booleanfalseWhether to hash the port**`ignore_path`**booleanfalseWhether to path of the URL (e.g. */foo/index.php*)**`ignore_query`**booleanfalseWhether to hash the query string parts in the URL (Keys *and* values).**`ignore_fragment`**booleanfalseWhether to hash the fragment / hash suffix in the URL or not.### Examples

[](#examples)

```
$reader = new \UrlFingerprint\FingerprintReader([
    'secret' => 's3cre7v4lu3',
    'ignore_host' => false,
]);
// Different hosts, but not part of the fingerprint. So both digest values are equal.
$fingerprint1 = $reader->capture('http://www.example.com/?foo');
$fingerprint2 = $reader->capture('http://www.example.net/?foo');
$reader->compare($fingerprint1, $fingerprint2); // true
```

```
$reader = new \UrlFingerprint\FingerprintReader([
    'secret' => 's3cre7v4lu3',
    'ignore_fragment' => true,
]);
// Create fingerprints for two equal URLs except the fragment
$fingerprint1 = $reader->capture('https://www.example.com/?foo');
$fingerprint2 = $reader->capture('https://www.example.com/?foo#bar');

// Fingerprints are not the same - The fragment part of the URL is taken into account.
$reader->compare($fingerprint1, $fingerprint2); // false
```

```
// Define query string keys which should be ignored and pass it as 2nd argument in the capture method.
$ignoreQuery = ['foo', 'baz'];

$fingerprint1 = $reader->capture('https://www.example.com/detail');
$fingerprint2 = $reader->capture('https://www.example.com/detail?foo=bar', $ignoreQuery);

// Fingerprints are equal because the 'foo' parameter is ignored
$reader->compare($fingerprint1, $fingerprint2); // true
```

Testing
-------

[](#testing)

With PHPUnit: `$ ./vendor/bin/phpunit tests`

Contributing
------------

[](#contributing)

If you notice bugs, have general questions or want to implement a feature, you are welcome to collaborate.

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Background
----------

[](#background)

### Motivation

[](#motivation)

Generating a unique hash from a URL is useful, e.g. when caching API responses or for security purposes. The simple idea of just hashing the URL can turn into a difficult task. Consider the following example:

```
$urlHash = hash('md5', 'https://example.com/index.php?foo=bar&qux');
```

This creates the same hash value (digest) each time. But what if the order of the query string parameters is changed?

```
$url1 = 'https://example.com/index.php?foo=bar&qux';
$url2 = 'https://example.com/index.php?qux&foo=bar';

hash_equals(
    hash('md5', $url1),
    hash('md5', $url2)
); // false :-(
```

Both URLs are technically the same, but the generated digest is different.

There are more parts of a URL that you may not want to include for the hash algorithm:

```
$url = 'https://example.com/#logo'; // Anchor / fragment appended
$url = 'http://example.com/'; // Different protocol
$url = 'https://example.com/?'; // Empty query string / No key/value groups
```

All three URLs could be similar according to your requirements and should therefore generate the same hash result. This is what this library was built for. There are other things in a URL that shouldn't affect the hash value of a URL:

- The order of the query parameters is different
- Another protocol is used
- URL-encoded characters such as `%20` should be taken into account

### Predecessor

[](#predecessor)

This library replaces the predecessor, the [url-signature library](https://github.com/dsentker/url-signature).

Compared to the url-signature library, this library is rewritten completely and should solve the following drawbacks:

- The url-signature library did not follow the Single-responsibility principle
- Arrays in query string could lead to unexpected results
- I do not like setting options with bitmask flags
- A thin wrapper for normalizing query strings is not required
- Insufficient investigation possibilities to debug the hash process

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance54

Moderate activity, may be stable

Popularity17

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

6

Last Release

1743d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9309c19594a948d4f0314bbdb2d3cfd56c84027fbd0fac6497c3e7098046be63?d=identicon)[dsentker](/maintainers/dsentker)

---

Top Contributors

[![dsentker](https://avatars.githubusercontent.com/u/86192?v=4)](https://github.com/dsentker "dsentker (2 commits)")

---

Tags

httppsr-7urlurihttpshostnamerfc3986parse\_urlrfc6570querystringquery-stringrfc3987parse\_strfingerprinting

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/dsentker-url-fingerprint/health.svg)

```
[![Health](https://phpackages.com/badges/dsentker-url-fingerprint/health.svg)](https://phpackages.com/packages/dsentker-url-fingerprint)
```

###  Alternatives

[league/uri

URI manipulation library

1.1k206.4M277](/packages/league-uri)[league/uri-interfaces

Common tools for parsing and resolving RFC3987/RFC3986 URI

538204.9M23](/packages/league-uri-interfaces)[guzzlehttp/psr7

PSR-7 message implementation that also provides common utility methods

8.0k1.0B3.2k](/packages/guzzlehttp-psr7)[react/http

Event-driven, streaming HTTP client and server implementation for ReactPHP

78126.4M414](/packages/react-http)[crwlr/url

Swiss Army knife for URLs.

11073.3k3](/packages/crwlr-url)[riimu/kit-urlparser

RFC 3986 compliant url parsing library with PSR-7 Uri component

33565.4k2](/packages/riimu-kit-urlparser)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
