Generate PDFs with Cloudflare and Laravel: A Step-by-Step Guide

Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

June 16, 2025 · 6 min read

Laravel and Cloudflare logos

Generating PDFs on the fly is a common requirement for many web applications, especially those dealing with invoices, reports, or any document-heavy functionalities. However, scaling this process can be a real headache, especially when your VPS starts struggling with numerous Puppeteer instances. In this tutorial, we’ll dive into how you can use Cloudflare’s Browser Rendering to handle PDF generation easily with Laravel.

Why Cloudflare for PDF Rendering?

Have you ever tried juggling multiple PDF generation tasks on your server only to watch it buckle under the pressure? It’s like trying to bake a dozen cookies in a toaster oven: possible, but not ideal. Cloudflare offers a more scalable solution by handling the heavy lifting of rendering PDFs, freeing up your server to focus on what it does best.

By offloading PDF rendering to Cloudflare, you benefit from:

  • Scalability: Handle numerous PDF requests without overloading your VPS.

  • Speed: Generate PDFs in under 200ms with Cloudflare’s efficient rendering.

  • Cost-Efficiency: Take advantage of Cloudflare’s generous usage limits, minimizing expenses.

cloudflare browser rendering at scale

Prerequisites

Before we start, make sure you have the following:

  • Laravel Installed: A fresh Laravel project set up.

  • Cloudflare Account: Sign up for a Cloudflare account if you haven’t already.

Installing Required Packages

We’ll be using Saloon PHP for handling API integrations. Saloon offers a fluent and intuitive way to interact with APIs, you can also use the default Laravel HTTP Client.

Install Saloon via Composer:

composer require saloonphp/saloon "^3.0"

# Make sure to also install the Laravel Plugin:
composer require saloonphp/laravel-plugin "^3.0"

Configuring Saloon PHP

Saloon allows us to define connectors and manage API interactions efficiently. We’ll create a new connector for Cloudflare.

Run the following Artisan command to generate a Saloon connector:

php artisan saloon:connector

This command scaffolds a basic connector structure for you. Let’s customize it for Cloudflare.

Creating the Cloudflare Connector

Navigate to the newly created connector file, typically found at app/Http/Integrations/Cloudflare/CloudflareConnector.php.

Here’s how your CloudflareConnector should look:

namespace App\Http\Integrations\Cloudflare;

use Saloon\Contracts\Authenticator;
use Saloon\Http\Auth\TokenAuthenticator;
use Saloon\Http\Connector;
use Saloon\Http\Response;
use Saloon\Traits\Plugins\AcceptsJson;

class CloudflareConnector extends Connector
{
    use AcceptsJson;

    /**
     * The Base URL of the API
     */
    public function resolveBaseUrl(): string
    {
        $accountId = config('services.cloudflare.account_id');

        return "https://api.cloudflare.com/client/v4/accounts/$accountId/browser-rendering";
    }

    protected function defaultAuth(): ?Authenticator
    {
        return new TokenAuthenticator(config('services.cloudflare.api_key'));
    }
}

Key Points:

  • Base URL: Customized to include your Cloudflare account ID.

  • Authentication: Uses TokenAuthenticator with your Cloudflare API key.

Obtaining Your Cloudflare Credentials

  1. Account ID:

    • Log in to your Cloudflare Dashboard.

    • Navigate to the Account Overview.

    • Locate your Account ID.

  2. API Token:

    • Go to My Profile > API Tokens.

    • Generate a new token with permissions for Browser Rendering.

Ensure you store these credentials securely, preferably in your .env file.

Updating Configuration

Add your Cloudflare credentials to config/services.php:

return [
	// ...rest of the config here

 	'cloudflare' => [
        'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
        'api_key' => env('CLOUDFLARE_API_KEY'),
    ]
]

Next, update your .env file with the new variables:

CLOUDFLARE_ACCOUNT_ID=your_account_id_here

CLOUDFLARE_API_KEY=your_api_key_here

Building the RenderPDF Request

With our connector in place, it’s time to define the request that will handle PDF rendering.

Generate a new Saloon request:

php artisan saloon:request

This creates a request class, typically at app/Http/Integrations/Cloudflare/Requests/RenderPDF.php.

Here’s a detailed look at our RenderPDF request:

namespace App\Http\Integrations\Cloudflare\Requests;

use Illuminate\Contracts\View\View;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasJsonBody;

class RenderPDF extends Request implements HasBody
{
    use HasJsonBody;
    /**
     * The HTTP method of the request
     */
    protected Method $method = Method::POST;

    public function __construct(
        public View $html,
    ) {}

    public function defaultBody(): array
    {
        return [
            'html' => $this->html->render(),
            'addScriptTag' => [
                [
                    'url' => 'https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4'
                ]
            ]
        ];
    }

    /**
     * The endpoint for the request
     */
    public function resolveEndpoint(): string
    {
        return '/pdf';
    }
}

What’s Happening Here:

  • HTTP Method: We’re using a POST request to send our HTML content.

  • Constructor: Accepts a Laravel View instance containing the HTML to be converted.

  • Default Body: Sends the rendered HTML and includes a script tag for Tailwind CSS to style the PDF.

By implementing HasJsonBody, we ensure our request body is correctly formatted as JSON, which Cloudflare expects.

Adding a helper method to our connector

Now that we have the RenderPDF request class, we like to add a helper method to our connector that calls the Request class with the required params. We can do this by editing our CloudflareConnector:

namespace App\Http\Integrations\Cloudflare;

use App\Http\Integrations\Cloudflare\Requests\RenderPDF;
use Illuminate\Contracts\View\View;
use Saloon\Contracts\Authenticator;
use Saloon\Http\Auth\TokenAuthenticator;
use Saloon\Http\Connector;
use Saloon\Http\Response;
use Saloon\Traits\Plugins\AcceptsJson;

class CloudflareConnector extends Connector
{
    use AcceptsJson;

    /**
     * The Base URL of the API
     */
    public function resolveBaseUrl(): string
    {
        $accountId = config('services.cloudflare.account_id');

        return "https://api.cloudflare.com/client/v4/accounts/$accountId/browser-rendering";
    }

    protected function defaultAuth(): ?Authenticator
    {
        return new TokenAuthenticator(config('services.cloudflare.api_key'));
    }

    public function render(View $html): Response
    {
        return $this->send(new RenderPDF($html));
    }
}

You can also call the Request on its own, but we prefer having the methods inside the integration as it makes the code a bit cleaner and easier to read.

Designing Blade Views for PDFs

Now, let’s create the Blade views that will serve as the HTML templates for our PDFs.

Creating the Print Layout

In resources/views/components/, create a new Blade component named print.blade.php:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style type="text/tailwindcss">
        @theme {
            --color-clifford: #da373d;
        }
    </style>
</head>
<body {{ $attributes->merge() }}>
{{ $slot }}
</body>
</html>

Highlights:

  • Tailwind CSS Integration: Using the Tailwind CDN for styling.

  • Custom Themes: The @theme directive allows for color customization, making your PDFs visually appealing.

Creating the Invoice Blade View

Next, create a print directory inside resources/views and add an invoice.blade.php file:

<x-print class="bg-gray-200">
    <div class="max-w-[85rem] px-4 sm:px-6 lg:px-8 mx-auto my-4 sm:my-10">
        @foreach($items as $item)
            <!-- Your invoice item markup here -->
        @endforeach
    </div>
</x-print>

Customization Tips:

  • Data Binding: Pass dynamic data such as $items to list invoice details.

  • Styling: Use Tailwind classes to style your PDF layout.

Feel free to expand upon this template with additional Blade directives and HTML as per your requirements.

Implementing the Invoice Controller

With our views ready, it’s time to create a controller that ties everything together.

Generate an invokable controller:

php artisan make:controller InvoiceController --invokable

Open app/Http/Controllers/InvoiceController.php and update it as follows:

namespace App\Http\Controllers;

use Exception;
use Illuminate\Http\Request;
use App\Http\Integrations\Cloudflare\CloudflareConnector;

class InvoiceController extends Controller
{
    /**
     * Handle the incoming request.
     */
    public function __invoke(Request $request, CloudflareConnector $cloudflare)
    {
        try {
            $pdf = $cloudflare->render(view('print.invoice', [
                'title' => 'Awesome PDF',
                'items' => $request->input('items', []),
            ]));

            return response($pdf, 200, [
                'Content-Type' => 'application/pdf',
            ]);
        } catch (Exception $e) {
            return response()->json([
                'error' => 'Failed to generate PDF',
                'message' => $e->getMessage()
            ], 500);
        }
    }
}

Key Features:

  • Dependency Injection: Injects the CloudflareConnector for sending render requests.

  • Error Handling: Uses a try-catch block to manage potential exceptions.

  • Dynamic Data: Passes data from the request to the Blade view for dynamic PDF content.

cloudflare to pdf

Routing the Controller

Define a route for your invoice PDF generation. In routes/web.php, add:

use App\Http\Controllers\InvoiceController;

Route::post('/generate-invoice', InvoiceController::class);

Now, your application is ready to handle invoice generation requests.

Handling Errors

Robust applications anticipate the unexpected. Let’s enhance our controller to handle errors more effectively.

In the InvoiceController, the try-catch block ensures that any issues during PDF generation are caught and a user-friendly error message is returned. You can further customize this by implementing specific exception handling based on Saloon’s capabilities.

For instance, you might want to handle network errors separately or provide retry mechanisms. Implementing such features ensures a smoother user experience, even when things go sideways.

Debugging Tips

  • Check Cloudflare Logs: Ensure that requests are being sent and received correctly.

  • Laravel Logs: Inspect storage/logs/laravel.log for any errors or warnings.

  • View Rendering: Verify that your Blade views render correctly by accessing them in a browser.

Optimizing for Performance

While Cloudflare handles the heavy lifting, optimizing your Laravel application ensures maximum efficiency.

Caching Blade Views

Render views efficiently by caching them. Laravel automatically caches views, but ensure you’re not introducing unnecessary complexities that might hinder this process.

Asynchronous Requests

Consider offloading PDF generation to Laravel Jobs, especially when these tasks are triggered by user actions that don’t require immediate feedback.

Monitoring Usage

Keep an eye on your Cloudflare usage to stay within the generous limits.

Conclusion

Scaling PDF generation in your Laravel application doesn’t have to be a daunting task. By using Cloudflare’s Browser Rendering, you offload the resource-intensive process of rendering PDFs, ensuring your VPS remains performant and responsive. With Saloon PHP handling API interactions and Laravel’s robust framework powering your application, generating PDFs becomes a breeze.

Whether you’re dealing with numerous invoices, reports, or any document-heavy functionalities, this setup offers a scalable, efficient, and cost-effective solution. Give your Laravel app the power of Cloudflare and easily handle your PDF generation process.

FAQs

Why should I use Cloudflare for PDF rendering instead of handling it on my server?

Using Cloudflare offloads the resource-intensive task of PDF rendering from your server, ensuring better scalability and performance. It also helps in reducing memory usage and handling high volumes of PDF generation without hitting server limits.

Can I customize the styling of my PDFs generated via Cloudflare?

By using Blade views and Tailwind CSS, you can design and style your PDFs to match your application’s requirements. Tailwind’s utility classes make it easy to create responsive and visually appealing layouts.

What happens if Cloudflare’s API limits are exceeded?

While Cloudflare offers generous limits, exceeding them may result in throttled requests or additional charges. It’s essential to monitor your usage and implement appropriate error handling to manage such scenarios.

Is Saloon PHP the only package I can use for this integration?

Saloon PHP is a great choice for handling API integrations in Laravel, but it’s not the only option. Depending on your preferences, you can also use the default Laravel HTTP Cl. However, Saloon provides a more structured and intuitive approach for API interactions.

How secure is the PDF generation process with Cloudflare?

Cloudflare ensures secure communication between your application and its services using HTTPS. Additionally, by using API tokens with specific permissions, you can maintain a high level of security and control over your PDF rendering processes.


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let’s get in touch.

Lucky Media is proud to be recognized as a leading Laravel Development Agency

Technologies

Laravel
Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

Founder and CEO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork

Related posts

March 21, 2025

How to maintain enterprise websites
Statamic
Business

March 13, 2025

Why do we use Astro?
Astro