tilleuls/url-signer-bundle
Symfony bundle for generating and validating signed URLs. Add tamper-proof, expiring links to routes, downloads, and API endpoints, protect resources from unauthorized access, and verify signatures through a simple, configurable service.
Architecture fit. The first and most important finding is a framework mismatch: tilleuls/url-signer-bundle is a Symfony bundle, not a Laravel package. It integrates via Symfony's dependency-injection container, a DependencyInjection/Extension config tree, and a kernel.request event subscriber — none of which exist in Laravel. The domain concept (time-limited, tamper-evident URLs) fits almost any PHP web application, but the artifact does not drop into a Laravel codebase without a port or adapter. Additionally, Laravel ships first-class signed URLs natively (URL::signedRoute, URL::temporarySignedRoute, URL::hasValidSignature, and the signed middleware alias). For a Laravel stack, the incremental value of this package is close to zero before any porting work is considered.
Integration feasibility. Three viable paths, in descending order of preference:
| Option | Effort | Risk | Notes |
|---|---|---|---|
| A. Use Laravel native signed URLs | ~0 | Low | Covers expiry, tamper detection, route scoping; maintained by the framework |
B. Adopt spatie/url-signer directly (the library this bundle wraps) |
Low | Low–Medium | Framework-agnostic; wrap in a Laravel service provider + middleware; gains custom payloads and cross-framework parity |
| C. Port/fork the bundle's logic | Medium | Medium–High | Inherits Symfony idioms, maintenance burden, security review obligation |
Option C is only justified if there is a hard requirement to run identical signing semantics across a Symfony service and a Laravel service in the same estate.
Technical risk.
composer require.FrameworkBundle/HttpKernel version constraints that are irrelevant (and potentially blocking) in a Laravel context.kid support, replay protection, clock-skew tolerance, revocation, and whether the secret is expected as a single global value (awkward for multi-tenant or per-tenant signing).Key questions for the maintainers / spike.
hash_equals for verification and bind the full URL (method, host, path, query) into the signature?Recommendation: Do not adopt the bundle. If the requirement is temporary signed URLs within Laravel, use the native implementation. If the requirement is a cross-framework signing contract, depend on the underlying framework-agnostic library — not this bundle — and own the ~150 lines of Laravel glue.
Stack fit. PHP 8.x / Composer: fine. Laravel 10–12: the bundle itself will not install cleanly (it expects symfony/framework-bundle and will pull Symfony components into a Laravel app, causing container and config conflicts). The underlying signer, if used, is a plain PHP dependency and fits without ceremony.
Migration path.
temporarySignedRoute, (b) spatie/url-signer behind a UrlSignerServiceProvider. Compare signature format, expiry semantics, and payload flexibility against the real requirement.UrlSigner contract, a singleton binding in a service provider, a ValidateSignedUrl middleware registered in bootstrap/app.php, and a signedUrl() helper. Keep the signing key in config/ sourced from the secret manager, never in .env checked into a repo.Compatibility. Verify the underlying library's PHP constraint against your runtime; verify whether expiry is Unix timestamp or DateTimeInterface; verify query-parameter naming, because a mismatch between producers and consumers is the most common failure in signed-URL systems. If any producer already emits URLs signed by the Symfony bundle, you must match its canonicalization exactly or you will silently break every existing link — treat this as a hard compatibility gate.
Sequencing. Spike → decision record → security review of the chosen implementation → internal contract/test vectors → integrate behind a feature flag on a single low-risk route → expand. Do not roll out to password reset or payment flows first; use a download or asset-link route as the canary.
Maintenance. MIT license removes legal friction. Operationally, the bundle becomes our code the moment we fork or port it. With 0 dependents and a small maintainer base, expect to own security patches. Budget a recurring review — at minimum, re-read the signer on each PHP major upgrade and on any advisory touching hash_*, random_*, or openssl_*.
Support. Community issues only; no SLA, no CVE process we can rely on. Internal escalation path must be "we patch it ourselves." Identify a named owner (one backend engineer) with the signing code in their on-call scope.
Scaling. The runtime cost is negligible — HMAC verification is stateless, sub-millisecond, and horizontally trivial. There is no datastore, no cache, no network hop. The real scaling concern is secret management: a single shared secret across N services and M environments, with rotation requiring dual-validation windows (accept old and new keys for the max link lifetime). If signed URLs will cross service or tenant boundaries, plan for kid-style key identifiers up front — retrofitting rotation is painful.
Failure modes to instrument.
Ramp-up. For a Symfony-fluent engineer: under an hour — the bundle's surface is small. For a Laravel team: the conceptual ramp-up is also under an hour, but the integration ramp-up is a day or two, because the work is entirely in the glue (provider, middleware, helper, tests) rather than in understanding the signer. The dominant cost is not learning the package; it is the security review and the decision to own the code.
How can I help you explore Laravel packages today?