Recommended Free Tools
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Use a Laravel queue to fetch and store a remote PDF, then let a separate authorized HTTP request download the stored file. A queue job cannot keep the original browser response open or deliver bytes directly; it moves the slow retrieval out of that request. The reliable flow is: create a download record, dispatch a job with stable identifiers, save the completed file to a configured filesystem disk, mark the record ready, and return it through a protected download endpoint.
This guide targets Laravel 13.x documentation reviewed on September 29, 2026, and covers downloading an existing remote PDF. Generating a new PDF is a different workflow.
How the queued PDF download works
- Create a request record. Store the authenticated owner, source URL, status, disk and eventual path.
- Dispatch a job. Pass the record ID and source information, not the PDF contents.
- Fetch in a worker. Apply a timeout, inspect the upstream response, validate the payload for your source, and write it to a private disk.
- Mark completion. Update status only after storage succeeds.
- Download later. An authorized endpoint uses the stored disk and path to return the file.
Laravel’s queue abstraction supports database, Amazon SQS, Redis, Beanstalkd, synchronous and null drivers; a connection identifies the backend and a connection can contain named queues. Laravel’s filesystem abstraction similarly lets a configured disk use local, SFTP or S3 storage. See the queue documentation and filesystem documentation.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsConfigure a queue and private storage
Local development
Set QUEUE_CONNECTION=database (or another driver) in .env, create the queue tables, and run migrations:
#1 Best Overall
php artisan make:queue-table
php artisan migrate
php artisan queue:work database --queue=pdf-downloads
Use a dedicated queue name when PDF retrieval should have its own worker capacity. The sync driver is useful for debugging because it runs immediately, but it does not test background execution.
Production choices
| Option | When it fits | Trade-off |
|---|---|---|
| Database | Already operating a relational database and modest throughput | Simple deployment, but jobs compete for database resources |
| Redis | Redis is already monitored and workers need fast queue operations | Requires Redis operations and capacity planning |
| Amazon SQS | Managed queue infrastructure is preferred | Visibility and retry behavior must be aligned with worker settings |
| Beanstalkd | Existing Beanstalkd environment | Another service to operate |
There is no universally best backend. Select one your team can monitor, secure and recover.
Filesystem disk
Configure a private disk in config/filesystems.php (for example, local or S3), and keep credentials in environment variables. Do not put confidential PDFs on a public disk merely to simplify downloads.
Create a download record
A minimal table should include user_id, source_url, disk, path, status, failure_reason, timestamps and, optionally, response metadata. Use statuses such as queued, processing, ready and failed. The record is the stable hand-off between the web request, worker and later download.
Dispatch from a controller
use AppJobsFetchRemotePdf;
use AppModelsPdfDownload;
use IlluminateHttpRequest;
public function store(Request $request)
{
$data = $request->validate([
'url' => ['required', 'url', 'max:2048'],
]);
$download = PdfDownload::create([
'user_id' => $request->user()->id,
'source_url' => $data['url'],
'disk' => 'local',
'status' => 'queued',
]);
FetchRemotePdf::dispatch($download->id)->onQueue('pdf-downloads');
return response()->json([
'id' => $download->id,
'status' => $download->status,
], 202);
}
Pass an ID rather than serializing a model with large or mutable data. In production, restrict outbound URLs to the sources your application is allowed to contact; otherwise a downloader can become a server-side request forgery risk.
Implement the queued fetch job
The example below uses Laravel’s HTTP client facade and filesystem API. Confirm exact HTTP-client behavior and options against the Laravel release installed in your application, especially if you need streaming for very large files.
namespace AppJobs;
use AppModelsPdfDownload;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationBusDispatchable;
use IlluminateHttpClientRequestException;
use IlluminateQueueInteractsWithQueue;
use IlluminateQueueSerializesModels;
use IlluminateSupportFacadesHttp;
use IlluminateSupportFacadesStorage;
use Throwable;
class FetchRemotePdf implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 3;
public $backoff = [30, 120, 300];
public $timeout = 300;
public function __construct(public int $downloadId) {}
public function handle(): void
{
$download = PdfDownload::findOrFail($this->downloadId);
$download->update(['status' => 'processing']);
$response = Http::timeout(120)->get($download->source_url);
$response->throw();
$body = $response->body();
if ($body === '') {
throw new RuntimeException('The upstream response was empty.');
}
// Validate according to your source. This basic check is not a substitute
// for MIME, signature, size and malware policy appropriate to your system.
if (! str_starts_with($body, '%PDF-')) {
throw new RuntimeException('The upstream body is not recognized as a PDF.');
}
$path = 'pdf-downloads/'.$download->id.'/document.pdf';
if (! Storage::disk($download->disk)->put($path, $body)) {
throw new RuntimeException('The PDF could not be stored.');
}
$download->update([
'path' => $path,
'status' => 'ready',
'failure_reason' => null,
]);
}
public function failed(Throwable $e): void
{
PdfDownload::whereKey($this->downloadId)->update([
'status' => 'failed',
'failure_reason' => $e->getMessage(),
]);
}
}
The PDF signature check is intentionally conservative: some valid delivery systems transform or wrap content, while a malicious response can begin with a PDF signature. Add content-length limits, MIME checks, malware scanning, redirect and authentication rules appropriate to your upstream. For large files, use a memory-conscious streaming design supported by your installed HTTP client and storage adapter rather than buffering the whole body; verify those APIs for your version.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Make retries safe
Retries can run after a timeout even when an upstream request partly succeeded. Write to an ID-specific path, overwrite only after a complete write, and update ready last. Consider deleting a partial object on failure and making the job idempotent when a ready record already has a verified file.
Rank #3
Return the completed PDF safely
use AppModelsPdfDownload;
use IlluminateHttpRequest;
use IlluminateSupportFacadesStorage;
public function download(Request $request, PdfDownload $download)
{
abort_unless($download->user_id === $request->user()->id, 404);
abort_unless($download->status === 'ready' && $download->path, 404);
abort_unless(Storage::disk($download->disk)->exists($download->path), 404);
return Storage::disk($download->disk)->download(
$download->path,
'document-'.$download->id.'.pdf',
['Content-Type' => 'application/pdf']
);
}
Laravel documents that Storage::download(path, filename, headers) generates a response that forces the browser to download the file. Authorize before revealing whether a path exists, and use a private disk for private documents. Where supported by the configured disk, temporaryUrl can provide an expiring link instead of proxying bytes through Laravel; set an expiry that matches your threat model and product flow.
Status endpoint and UI
Expose only the owner’s record and return queued, processing, ready or failed. Poll or use your existing notification system. Show the download link only for ready; show a retry action or support path for failed. Never treat dispatch success as file completion.
Worker lifecycle, timeouts and cleanup
Run a supervised worker in production and monitor failed jobs. Set job timeout, attempts and backoff from measured retrieval and storage time, upstream limits and worker resources. A job timeout should not be equal to or greater than the queue connection’s retry or visibility interval without understanding duplicate execution risk. Laravel documents worker timeouts, attempts, retries and failed-job handling, but no universal numeric value fits every PDF.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchSchedule cleanup for expired records and stored objects, and log the upstream host, status, elapsed time and resulting path without logging secrets or sensitive PDF contents. Keep retention, encryption, access logs and deletion requirements explicit.
Rank #4
Troubleshooting
The request returns 202 but no file appears
A 202 means the job was accepted. Confirm a worker is running with the same environment, connection and queue name: php artisan queue:work database --queue=pdf-downloads. Inspect failed jobs and worker logs.
The job fails with a timeout
Measure DNS, connection, download and storage time separately. Check upstream limits and worker CPU/memory, then adjust timeout and visibility settings together. Do not simply make retries infinite.
The downloaded file is HTML
Many login pages, bot checks and error pages return HTTP 200. Inspect status, headers, final URL and the body signature; require authentication headers or cookies when legitimately needed, and reject unexpected content.
Users can access another user’s PDF
Enforce ownership or policy authorization before checking status or calling download. Do not expose raw object keys or public URLs for private documents.
Best Value
Duplicate files appear after retries
Use deterministic paths keyed by the download ID, make writes idempotent, and update the database only after the final object exists. Investigate queue visibility and worker timeout alignment.
Memory usage is excessive
$response->body() buffers the payload. For large PDFs, implement documented streaming APIs for your exact Laravel and HTTP-client versions, enforce a maximum size, and test interruption and cleanup behavior.
Or skip the browser setup:
If your queued workflow needs screenshots or PDFs of web pages rather than an existing PDF URL, ScreenshotNeo provides a one-call API. It removes cookie/consent banners, newsletter popups and chat widgets before capture; bot checks, blank pages, failed loads and cache hits are not billed, and response headers identify the page verdict and billing. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for options such as PDF paper size, margins, page ranges, waits, headers, cookies, selectors and webhooks. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Can a queued job return the PDF directly?
No. The browser request and worker execution are separate. Return a request ID, then download after the record is ready.
Is this the same as queued PDF generation?
No. This pattern retrieves an existing remote PDF. Packages that queue PDF generation address rendering and have different APIs and constraints.
Should I use a temporary URL?
Use one when your configured disk supports it and an expiring, direct object link fits your authorization model; otherwise stream through an authorized controller.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

