Skip to main content

Purpose of this page

When you want a time-limited share link to use exactly the conditions set when it was issued, you need to do more than verify the URL signature. You also need to be consistent about where you read the values from. This page walks through an example that includes a report type and output format in the URL and returns preview JSON. This page covers Laravel 13. For URL generation in general, see URL generation. For the basics of retrieving input, see HTTP requests.
A signature does not protect the entire request. Form and JSON request bodies are not signed. After the signature is verified, retrieve signed query values using methods such as $request->query('format'). Methods such as input() and all(), as well as dynamic properties, may return unsigned body values. This distinction matters for GET requests too.

What the signature protects

input() is an API that reads not only the query string but also the request body. When Laravel merges these inputs, body values take precedence over query string values. query(), on the other hand, reads only the query string. A successful signature check and the fact that a value you read afterward is signed are two separate things to verify.

1. Define the route to verify

In routes/web.php, define a named route that requires a signature. This example does not touch the database or files. It simply returns the conditions it received.
  • The signed middleware verifies the signature and expiration before the closure runs. If either is invalid, it returns 403.
  • query() explicitly reads the values used for processing from the query string.
  • Validation runs against the retrieved array. Signature verification does not guarantee value types or business-level allowed ranges.
In this example, only the values extracted with query() are passed to Validator::make(). If you replace them with $request->all() in code meant to validate signed query values, you end up handling input that includes the request body.

2. Generate the URL in a trusted issuing process

After registering the named route, use the following code in the process that issues links. Because report and format are not path placeholders, they are appended to the query string.
In addition to the conditions you specified, this URL contains expires and signature. expires and signature are reserved parameters used by Laravel, so don’t pass them as names for your own business parameters. If you expose the issuing process in a real application, don’t let anyone issue links with arbitrary conditions. Apply the necessary authentication and authorization.

3. Check normal usage

If you open the issued URL as is within its validity period, the following JSON is returned.
Once the link expires, the route’s signed middleware returns 403. Signed URLs are not single-use tokens. In this example, the same URL can be reused while it is still valid. If you want a link to be used only once, you need to track its used state separately from the signature.

Separating values excluded from verification

If you want to allow values such as page numbers added by the frontend, you can exclude them from signature verification with hasValidSignatureWhileIgnoring(['page']). However, anyone can change excluded values, so don’t treat them as signed conditions. For example, page for display purposes plays a different role from report and format, which are fixed at issuance. Don’t exclude values used to decide the target data, recipients, pricing, and so on just for convenience.
The example above uses the standard signed middleware, so it does not exclude page either. If you want to add query parameters, decide first what the generating side and the verifying side will treat as signed.

Using relative signatures

If your design does not include the domain in the signature, combine absolute: false at generation time with signed:relative at verification time.
In this case, change middleware('signed') in the earlier route to middleware('signed:relative'). Request bodies are not signed with relative signatures either, so you should still retrieve values with query().

Implementation checklist

  • The receiving route requires signature verification.
  • Signed query values are retrieved with query().
  • Value format and allowed ranges are validated separately.
  • Values excluded from signature verification are not used as conditions fixed at issuance.
  • Absolute and relative signature settings match on the generating and verifying sides.
  • Knowing the URL is not treated as equivalent to the logged-in user’s permissions.
Last modified on October 16, 2026