PHPackages                             yorcreative/laravel-urlshortener - 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. [Framework](/categories/framework)
4. /
5. yorcreative/laravel-urlshortener

ActiveLibrary[Framework](/categories/framework)

yorcreative/laravel-urlshortener
================================

A laravel url shortener package that provides internal url redirects with passwords, url expirations, open limits before expiration and click tracking out of the box.

v3.2.0(2w ago)12212.8k↓29.3%21MITPHPPHP ^8.1CI passing

Since Aug 14Pushed 2w ago3 watchersCompare

[ Source](https://github.com/YorCreative/Laravel-UrlShortener)[ Packagist](https://packagist.org/packages/yorcreative/laravel-urlshortener)[ RSS](/packages/yorcreative-laravel-urlshortener/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (10)Dependencies (15)Versions (16)Used By (0)

 [ ![Logo](content/logo.png) ](https://github.com/YorCreative)

### Laravel URL Shortener

[](#laravel-url-shortener)

[![GitHub license](https://camo.githubusercontent.com/ac1771dfb46823ccdcce1ccb18f8aaeb683ec1769d0eeb8c486c542c0c1be049/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f596f7243726561746976652f4c61726176656c2d55726c53686f7274656e6572)](https://github.com/YorCreative/Laravel-UrlShortener/blob/main/LICENSE.md)[![GitHub stars](https://camo.githubusercontent.com/4141d712b9449ccb3f7ca064b0e7274c80f9d9700f646df97fe3e4defa708b9a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f596f7243726561746976652f4c61726176656c2d55726c53686f7274656e6572)](https://github.com/YorCreative/Laravel-UrlShortener/stargazers)[![GitHub issues](https://camo.githubusercontent.com/5d8eb368ab53e0d8d01bcc9204df1d6da3ea7b139bf8c845206f23974ad63ce7/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f596f7243726561746976652f4c61726176656c2d55726c53686f7274656e6572)](https://github.com/YorCreative/Laravel-UrlShortener/issues)[![GitHub forks](https://camo.githubusercontent.com/02b491c784614908336d47e70727b99f5b19d14b8d7bacce10a8f0d1a1d20a16/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f596f7243726561746976652f4c61726176656c2d55726c53686f7274656e6572)](https://github.com/YorCreative/Laravel-UrlShortener/network)[![Packagist Downloads](https://camo.githubusercontent.com/63dfc0e4c018f553c56a832e2da0669644cc7f3883e88f6bc0346e7b2feb07b7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f596f7243726561746976652f4c61726176656c2d55726c53686f7274656e65723f636f6c6f723d677265656e)](https://camo.githubusercontent.com/63dfc0e4c018f553c56a832e2da0669644cc7f3883e88f6bc0346e7b2feb07b7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f596f7243726561746976652f4c61726176656c2d55726c53686f7274656e65723f636f6c6f723d677265656e)[![PHPUnit](https://github.com/YorCreative/Laravel-UrlShortener/actions/workflows/phpunit.yml/badge.svg)](https://github.com/YorCreative/Laravel-UrlShortener/actions/workflows/phpunit.yml)

A Laravel URL Shortener package that provides URL redirects with optionally protected URL password, URL expiration, open limits before expiration, ability to set feature activation dates, click tracking, custom vanity identifiers, event dispatching, and soft delete/restore out of the box for your Laravel applications.

Requirements
------------

[](#requirements)

- PHP 8.1+ (PHP 8.3+ when using Laravel 13)
- Laravel 10.x, 11.x, 12.x, or 13.x

Installation
------------

[](#installation)

install the package via composer:

```
composer require yorcreative/laravel-urlshortener
```

Publish the packages assets.

```
php artisan vendor:publish --provider="YorCreative\UrlShortener\UrlShortenerServiceProvider"
```

You can also publish assets by tag.

```
php artisan vendor:publish --tag=urlshortener-config
php artisan vendor:publish --tag=urlshortener-views
php artisan vendor:publish --tag=urlshortener-migrations
```

Run migrations.

```
php artisan migrate
```

Upgrade Guides
--------------

[](#upgrade-guides)

[Upgrading to v3.x from v2.x](https://github.com/YorCreative/Laravel-UrlShortener/wiki/Upgrading-to-3.x-from-2.x)

[Upgrading to v2.x from v1.x](https://github.com/YorCreative/Laravel-UrlShortener/wiki/Upgrading-to-2.x-from-1.x)

Usage
-----

[](#usage)

Building Short Urls

```
/**
 * Basic
 */
$url = UrlService::shorten('https://something-extremely-long.com/even/longer?ref=with&some=thingelselonger')
        ->build();
// http(s)://host/prefix/identifier

/**
 * Advanced
 */
$url = UrlService::shorten('https://something-extremely-long.com/even/longer?ref=with&some=thingelselonger')
        ->withActivation(Carbon::now()->addHour()->timestamp)
        ->withExpiration(Carbon::now()->addDay()->timestamp)
        ->withOpenLimit(2)
        ->withOwnership(Model::find(1))
        ->withPassword('password')
        ->withTracing([
            'utm_id' => 't123',
            'utm_campaign' => 'campaign_name',
            'utm_source' => 'linkedin',
            'utm_medium' => 'social',
        ])
        ->build();
// http(s)://host/prefix/identifier

/**
 * Custom (Vanity) Identifier
 */
$url = UrlService::shorten('https://example.com/my-product-launch')
        ->withIdentifier('launch2025')
        ->build();
// http(s)://host/prefix/launch2025
```

Finding Existing Short Urls

```
/**
 * Find a Short URL by its identifier
 */
$shortUrl = UrlService::findByIdentifier('identifier');
// returns instance of ShortUrl Model.

/**
 * Find a Short URL by its hashed signature
 */
$shortUrl = UrlService::findByHash(md5('long_url'));
// returns instance of ShortUrl Model.

/**
 * Find a Short URL by its plain text long url string
 */
$shortUrl = UrlService::findByPlainText('long_url');
// returns instance of ShortUrl Model.

/**
 * Find or Create - returns existing ShortUrl if found, or UrlBuilder for new creation
 * This is useful when you want to avoid exceptions for duplicate URLs
 */
$result = UrlService::findOrCreate('long_url');
// returns ShortUrl if exists, or UrlBuilder if new

// Usage example:
$result = UrlService::findOrCreate('https://example.com/my-long-url');
if ($result instanceof ShortUrl) {
    // URL already exists, use existing short URL
    $shortUrl = $result;
} else {
    // New URL, continue building with options
    $shortUrl = $result->withExpiration(Carbon::now()->addWeek()->timestamp)->build();
}

/**
 * Find shortUrls by UTM combinations.
 *
 * Note* This method only accepts the following array fields:
 *  - utm_id
 *  - utm_campaign
 *  - utm_source
 *  - utm_medium
 *  - utm_content
 *  - utm_term
 */
$shortUrlCollection = UrlService::findByUtmCombination([
    'utm_campaign' => 'alpha',
    'utm_source' => 'bravo',
    'utm_medium' => 'testing'
])
// returns an instance of Eloquent Collection of ShortUrl Models.
```

Deleting &amp; Restoring Short Urls

```
/**
 * Soft delete a Short URL by identifier
 */
UrlService::delete('identifier');

// With multi-domain support
UrlService::delete('identifier', 'short.io');

/**
 * Restore a soft-deleted Short URL
 */
UrlService::restore('identifier');

// With multi-domain support
UrlService::restore('identifier', 'short.io');
```

Getting Click Information

```
$clicks = ClickService::get()->toArray();

dd($clicks);
[
    'results' => [
        [
            'id' => ...,
            'created_at' => ...,
            'short_url' => [
                'id' => ...,
                'identifier' => ...,
                'hashed' => ...,
                'plain_text' => ...,
                'limit' => ...,
                'tracing' => [
                    'id' => ...,
                    'utm_id' => ...,
                    'utm_source' => ...,
                    'utm_medium' => ...,
                    'utm_campaign' => ...,
                    'utm_content' => ...,
                    'utm_term' => ...,
                ]
                'created_at' => ...,
                'updated_at' => ...
            ],
            'location' => [
                'id' => ...,
                'ip' => ...,
                'countryName' => ...,
                'countryCode' => ...,
                'regionCode' => ...,
                'regionName' => ...,
                'cityName' => ...,
                'zipCode' => ...,
                'isoCode' => ...,
                'postalCode' => ...,
                'latitude' => ...,
                'longitude' => ...,
                'metroCode' => ...,
                'areaCode' => ...,
                'timezone' => ...,
                'created_at' => ...,
                'updated_at' => ...
            ],
            'outcome' => [
                'id' => ...,
                'name' => ...,
                'alias' => ...,
            ],
        ]
    ],
    'total' => 1
];
```

Getting Click Information and Filtering on Ownership

```
$clicks = ClickService::get([
    'ownership' =>  [
        Model::find(1),
        Model::find(2)
    ]
]);
```

Filter on Outcome

```
$clicks = ClickService::get([
    'outcome' => [
        1, // successful_routed
        2, // successful_protected
        3, // failure_password
        4, // failure_limit
        5, // failure_expiration
        6, // failure_activation
    ]
]);
```

Filter on the Click's YorShortUrl Status

```
$clicks = ClickService::get([
    'status' => [
        'active',
        'expired',
        'expiring' // within 30 minutes of expiring
    ]
]);
```

Filtered on YorShortUrl Identifier(s)

```
$clicks = ClickService::get([
    'identifiers' => [
         'xyz',
         'yxz'
    ]
]);
```

Filtered Clicks by UTM parameter(s). These Can be filtered together or individually.

```
$clicks = ClickService::get([
    'utm_id' => [
         'xyz',
         'yxz'
    ],
    'utm_source' => [
         'linkedin',
         'facebook'
    ],
    'utm_medium' => [
         'social'
    ],
    'utm_campaign' => [
         'sponsored',
         'affiliate'
    ],
    'utm_content' => [
         'xyz',
         'yxz'
    ],
    'utm_term' => [
         'marketing+software',
         'short+url'
    ],
]);
```

Iterate Through Results With Batches

```
$clicks = ClickService::get([
    'limit' => 500,
    'offset' => 1500,
]);

$clicks->get('results');
$clicks->get('total');
```

Putting it all Together

```
/**
 * Get the successfully routed clicks for all active short urls that are owned by Model IDs 1,2,3 and 4.
 * Set the offset of results by 1500 clicks and limit by the results by 500.
 */
$clicks = ClickService::get([
    'ownership' => Model::whereIn('id', [1,2,3,4])->get()->toArray(),
    'outcome' => [
        1, // successful_routed
    ],
    'status' => [
        'active',
    ],
    'utm_campaign' => [
        'awareness',
    ],
    'utm_source' => [
        'github',
    ],
    'limit' => 500,
    'offset' => 1500,
]);
```

UTM Support
-----------

[](#utm-support)

When creating a Short URL, the following UTM parameters are available to attach to the Short URL for advanced tracking of your Short Urls.

- utm\_id
- utm\_campaign
- utm\_source
- utm\_medium
- utm\_content
- utm\_term

UTM information is hidden in the Short URL identifier and clicks are filterable by UTM parameters.

Events
------

[](#events)

The package dispatches events that you can listen to in your application:

EventDispatched WhenPayload`ShortUrlCreated`A new short URL is built`ShortUrl $shortUrl`, `string $builtUrl``ShortUrlClicked`A short URL is clicked`string $identifier`, `int $outcomeId`, `string $requestIp`, `?string $domain``ShortUrlExpired`An expired short URL is accessed`ShortUrl $shortUrl`, `string $identifier`, `?string $domain````
// EventServiceProvider or listener registration
use YorCreative\UrlShortener\Events\ShortUrlCreated;
use YorCreative\UrlShortener\Events\ShortUrlClicked;
use YorCreative\UrlShortener\Events\ShortUrlExpired;

// Example listener
Event::listen(ShortUrlCreated::class, function (ShortUrlCreated $event) {
    Log::info('Short URL created', [
        'identifier' => $event->shortUrl->identifier,
        'url' => $event->builtUrl,
    ]);
});
```

Multi-Domain Support
--------------------

[](#multi-domain-support)

v3 introduces multi-domain support, allowing you to host short URLs on multiple domains with per-domain configuration.

### Enabling Multi-Domain

[](#enabling-multi-domain)

Set the environment variable or update your config:

```
URL_SHORTENER_MULTI_DOMAIN=true
```

### Configuration

[](#configuration)

```
// config/urlshortener.php
'domains' => [
    'enabled' => env('URL_SHORTENER_MULTI_DOMAIN', false),
    'default' => env('URL_SHORTENER_DEFAULT_DOMAIN', env('APP_URL')),
    'resolution_strategy' => 'host', // 'host', 'subdomain', or 'path'

    'hosts' => [
        'short.io' => [
            'prefix' => 's',
            'identifier_length' => 4,
            'redirect_code' => 301,
        ],
        'link.company.com' => [
            'prefix' => null, // No prefix
            'identifier_length' => 8,
        ],
    ],

    'aliases' => [
        'www.short.io' => 'short.io',
    ],
],
```

### Building Short URLs for Specific Domains

[](#building-short-urls-for-specific-domains)

```
// Specify domain explicitly
$url = UrlService::shorten('https://example.com/long-url')
    ->forDomain('short.io')
    ->build();
// Returns: https://short.io/s/abc123

// Use current request's domain
$url = UrlService::shorten('https://example.com/long-url')
    ->forCurrentDomain()
    ->build();

// Custom prefix override
$url = UrlService::shorten('https://example.com/long-url')
    ->forDomain('short.io')
    ->withPrefix('custom')
    ->build();

// Custom prefixes must be registered so package routes can resolve them:
// config/urlshortener.php
'routing' => [
    'additional_prefixes' => ['custom'],
],

// Custom identifier length
$url = UrlService::shorten('https://example.com/long-url')
    ->forDomain('short.io')
    ->withIdentifierLength(8)
    ->build();
```

### Domain-Aware Lookups

[](#domain-aware-lookups)

```
// Find by identifier on specific domain
$shortUrl = UrlService::findByIdentifier('abc123', 'short.io');

// Find all URLs for a domain
$shortUrls = UrlService::findByDomain('short.io');

// Find or create with domain
$result = UrlService::findOrCreate('https://example.com', 'short.io');
```

### Same Identifier on Different Domains

[](#same-identifier-on-different-domains)

With multi-domain enabled, the same identifier can exist on different domains pointing to different URLs:

```
// Both can coexist
UrlService::shorten('https://site-a.com')->forDomain('short.io')->build();
// https://short.io/s/abc123 -> https://site-a.com

UrlService::shorten('https://site-b.com')->forDomain('link.co')->build();
// https://link.co/abc123 -> https://site-b.com
```

Security Features
-----------------

[](#security-features)

### URL Validation

[](#url-validation)

v3 includes built-in URL validation to prevent open redirect and SSRF attacks. This is disabled by default for backwards compatibility, but **recommended for new installations**.

```
# Enable in .env (recommended)
URL_SHORTENER_VALIDATE_URLS=true
```

```
// config/urlshortener.php
'url_validation' => [
    'enabled' => env('URL_SHORTENER_VALIDATE_URLS', false),
    'allowed_schemes' => ['http', 'https'],
    'block_private_ips' => env('URL_SHORTENER_BLOCK_PRIVATE_IPS', true),
    'resolve_dns_private_ips' => env('URL_SHORTENER_RESOLVE_DNS_PRIVATE_IPS', true),
    'blocked_hosts' => [
        // 'internal.company.com',
    ],
    'block_metadata_endpoints' => env('URL_SHORTENER_BLOCK_METADATA', true),
],
```

**Protected against:**

- `javascript:` protocol (XSS)
- `data:` protocol (XSS)
- `file:` protocol (local file access)
- Private IP ranges (SSRF)
- Cloud metadata endpoints (SSRF)
- Localhost redirects

When DNS private IP checks are enabled, hostnames are resolved during URL creation and any private or reserved resolved IP is rejected. DNS lookup failures or hosts with no IP records are allowed, so add known internal hostnames to `blocked_hosts` when they should always be rejected.

### Rate Limiting for Password-Protected URLs

[](#rate-limiting-for-password-protected-urls)

Brute-force protection is automatically enabled for password-protected short URLs:

```
// config/urlshortener.php
'protection' => [
    'rate_limit' => [
        'max_attempts' => env('URL_SHORTENER_PASSWORD_MAX_ATTEMPTS', 5),
        'decay_minutes' => env('URL_SHORTENER_PASSWORD_DECAY_MINUTES', 1),
    ],
],
```

After exceeding the maximum attempts, users receive a `429 Too Many Requests` response with a `Retry-After` header.

Environment Variables
---------------------

[](#environment-variables)

VariableDefaultDescription`URL_SHORTENER_MULTI_DOMAIN``false`Enable multi-domain support`URL_SHORTENER_DEFAULT_DOMAIN``APP_URL`Default domain for short URLs`URL_SHORTENER_RESOLUTION_STRATEGY``host`How to resolve domain from request`URL_SHORTENER_VALIDATE_DOMAIN``true`Validate requests against configured domains`URL_SHORTENER_DOMAINS_DATABASE``false`Store domain config in database`URL_SHORTENER_VALIDATE_URLS``false`Enable URL validation (recommended)`URL_SHORTENER_BLOCK_PRIVATE_IPS``true`Block private/internal IPs`URL_SHORTENER_RESOLVE_DNS_PRIVATE_IPS``true`Resolve hostnames and block private/reserved IP results`URL_SHORTENER_BLOCK_METADATA``true`Block cloud metadata endpoints`URL_SHORTENER_PASSWORD_MAX_ATTEMPTS``5`Max password attempts before rate limit`URL_SHORTENER_PASSWORD_DECAY_MINUTES``1`Minutes until rate limit resetsTesting
-------

[](#testing)

```
composer test
```

Credits
-------

[](#credits)

- [Yorda](https://github.com/yordadev)
- [All Contributors](../../contributors)

###  Health Score

59

—

FairBetter than 98% of packages

Maintenance97

Actively maintained with recent releases

Popularity43

Moderate usage in the ecosystem

Community18

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 87.5% 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 ~140 days

Recently: every ~219 days

Total

11

Last Release

14d ago

Major Versions

v1.0.3 → v2.0.02022-09-07

v2.1.2 → v3.0.02026-01-16

PHP version history (2 changes)v1.0.0PHP ^8.0

v3.0.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/51f87d3b079a2d52ebbc2c1b71c65fe3d29717a0122436ef17f55e687ccaa1f4?d=identicon)[yordadev](/maintainers/yordadev)

---

Top Contributors

[![yordadev](https://avatars.githubusercontent.com/u/28032848?v=4)](https://github.com/yordadev "yordadev (21 commits)")[![gnixner](https://avatars.githubusercontent.com/u/44546560?v=4)](https://github.com/gnixner "gnixner (1 commits)")[![onurcanalp](https://avatars.githubusercontent.com/u/4402314?v=4)](https://github.com/onurcanalp "onurcanalp (1 commits)")[![spekulatius](https://avatars.githubusercontent.com/u/8433587?v=4)](https://github.com/spekulatius "spekulatius (1 commits)")

---

Tags

hacktoberfesthacktoberfest2022laravellaravel-packagelaravel10laravel9lumen-frameworkpackagephpshorten-urlsshortener-serviceshortener-urlshorturlshorturl-packageshorturl-servicesurl-shortenerurlsurlframeworklaravelshortenerurl shortenerlaravel url shortenerlaravel url

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/yorcreative-laravel-urlshortener/health.svg)

```
[![Health](https://phpackages.com/badges/yorcreative-laravel-urlshortener/health.svg)](https://phpackages.com/packages/yorcreative-laravel-urlshortener)
```

###  Alternatives

[laravel/horizon

Dashboard and code-driven configuration for Laravel queues.

4.2k95.4M306](/packages/laravel-horizon)[laravel/sail

Docker files for running a basic Laravel application.

1.9k205.7M1.3k](/packages/laravel-sail)[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k108.5M886](/packages/laravel-socialite)[rebing/graphql-laravel

Laravel wrapper for PHP GraphQL

2.2k7.7M34](/packages/rebing-graphql-laravel)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M194](/packages/laravel-ai)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M151](/packages/laravel-mcp)

PHPackages © 2026

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