jeffersongoncalves/laravel-short-url
Laravel Short URL is a package for creating and managing short links in Laravel applications. Generate compact URLs, customize aliases, and handle redirects through a simple, framework-friendly API so you can add short-link functionality quickly. Ideal for campaigns, dashboards, and internal tools.
The package registers short-URL routes for you; there is very little setup before you can redirect a slug.
composer require <vendor>/<package> then php artisan vendor:publish --tag=short-url-config (or the package's config tag). Check the published config/short-url.php first — particularly the route block.php artisan migrate), then create your first URL:
use AshAllenDesign\ShortURL\Facades\ShortURL; // or the package's facade/helper
$shortURL = ShortURL::destinationUrl('https://example.com/some/long/path')->make();
echo $shortURL->default_short_url;
short-url.route.fallback is enabled and that APP_URL / the route prefix match what you're requesting. Note that with the fallback enabled, any unmatched path — including GET / on an app with no root route — is now answered by the fallback, so a stray 404 on / is expected behaviour rather than a broken install (5.2.4).First use case for most apps: generating shareable links from a controller (invite links, campaign URLs, signed download links). Confirm the destination_url is normalized as you expect before you wire it into anything user-facing.
Redirect-only vs. tracked URLs. For throwaway links, the minimal destinationUrl(...)->make() call is enough. For anything you want to measure, add tracking options before make() (->trackVisits(), ->trackIPAddress(), ->trackDeviceType() etc.) — these are typically opt-in at creation time, so decide per link rather than globally.
Key-based lookup in your own controllers. The package is usually paired with a route that resolves a slug. Wrap resolution in a try/catch for the package's "not found" exception and translate it into a 404 of your own, so behaviour is consistent with the rest of your app:
try {
$url = ShortURL::findByKey($key);
} catch (\Throwable $e) {
abort(404);
}
return redirect()->away($url->destination_url, $url->redirect_status_code ?? 302);
Middleware and fallback route precedence (5.2.3–5.2.4 behaviour). The fallback route is deliberately outside the web middleware group. Host-app routes always win because they are registered first; the fallback only catches what is left over. That means:
Route::post('/short/{key}') still runs full web middleware, including CSRF — good, keep it if you need authenticated or validated behaviour.QUERY requests (404 instead of 405) and to non-GET requests on the root path (POST / now returns 404 rather than throwing a TypeError → 500). If you previously relied on 405 responses to detect misconfigured clients, or on a 500 on POST / as a smoke signal that something was routing oddly, both signals are gone.VerifyCsrfToken::$except entry added solely to silence 419s on the short-url prefix, remove it and retest.Route registration order. In RouteServiceProvider, register the package's routes after your application routes if you want your routes to take precedence in edge cases. Most projects don't need this, but it matters when you mount your own handlers under the same prefix. It also matters if you do have a GET / route: registering your root route normally keeps it winning over the fallback.
Environment parity. Keep short-url.route.fallback on in local and staging as well as production, otherwise the 404-vs-405 behaviour, the root-path handling, and any client behaviour that depends on it will only surface in production.
Unmatched traffic now uniformly yields 404 — including the root path. 5.2.3 changed non-GET requests on the short-url prefix from 405 to 404; 5.2.4 extends that to QUERY requests and fixes a TypeError/500 that POST / (and other non-GET root requests) raised when the fallback was enabled. Audit any client, API consumer, monitoring probe, or test suite that asserts on status codes for the short-url prefix or for /:
->post('/s/abc')->assertStatus(405) must become assertNotFound().POST / returns 500 (or any 4xx other than 404) must become assertNotFound().POST / 500s as an uptime alert now needs a different signal, or it will silently pass.Fallback + CSRF is the important safety win. Any request that reaches the fallback is answered without the web middleware, so CSRF can't intercept it. If you have custom middleware in the web group that mutates responses globally (e.g. security headers, locale switching), it will not apply to fallback-served redirects — nor to fallback-served 404s. Register any truly global headers as terminable/global middleware if you need them on short-URL responses too.
Root-route collisions. Now that the fallback reliably claims / for non-GET verbs (and for GET / when you have no root route), be explicit about intent: if you want POST / handled by your app, define that route in your app (it wins by registration order); if you expect a 404, you'll get the fallback's 404. Don't rely on the previous 500 as a canary.
Host routes keep precedence — but only if registered first. If a route in your app is not matching and you're getting a package redirect instead, the cause is almost always registration order or a prefix collision, not the fallback logic. Check php artisan route:list --path=your-prefix (and --path=/ for the root case) to see the resolved order.
Status codes are per-URL data, not config. The redirect status (302 vs 301) lives on the short URL record. Changing a default in config does not rewrite existing rows; write a one-off migration/console command if you need to bulk-change. 301s are cached aggressively by browsers — avoid them for anything you might need to revoke.
Debugging missed redirects. Three usual suspects, in order: (1) the key column doesn't match the URL you're requesting (case-sensitivity or extra path segments); (2) the URL is deactivated (activated_at, deactivated_at) and the package is silently treating it as not-found; (3) fallback is disabled and no route exists for the prefix. Log the resolved key and the lookup result rather than the whole model. A 404 that "used to be a 405/500" is almost always the fallback doing its job, not a regression.
Extension points worth knowing. The destination URL builder and the visit model are typically swappable via config; if you need custom tracking dimensions, prefer extending the visit model and adding a custom builder method over forking the package. Also confirm the package's migrations are published — running them from the vendor directory works until you need to alter a column.
Rate limiting. The fallback route is unauthenticated by nature and now also answers root-path traffic. If the prefix is guessable, add a throttle middleware to a host-defined fallback route that calls into the package, or accept that slug enumeration is possible and make slugs long/random.
How can I help you explore Laravel packages today?