How it works | laravel-og-image | Spatie

 SPATIE

  Laravel Open Graph Image
===========================

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Laravel-og-image](https://spatie.be/docs/laravel-og-image/v1)  Basic-usage  How it works

 Version   v1

 Other versions for crawler [v1](https://spatie.be/docs/laravel-og-image/v1)

- [ Introduction ](https://spatie.be/docs/laravel-og-image/v1/introduction)
- [ Requirements ](https://spatie.be/docs/laravel-og-image/v1/requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/laravel-og-image/v1/installation-setup)
- [ Support us ](https://spatie.be/docs/laravel-og-image/v1/support-us)
- [ Questions and issues ](https://spatie.be/docs/laravel-og-image/v1/questions-issues)
- [ Changelog ](https://spatie.be/docs/laravel-og-image/v1/changelog)
- [ About us ](https://spatie.be/docs/laravel-og-image/v1/about-us)

Basic usage
-----------

- [ How it works ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/how-it-works)
- [ Getting started ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/getting-started)
- [ Customizing screenshots ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/customizing-screenshots)
- [ Defining fallback images ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/defining-fallback-images)
- [ Caching and storage ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/managing-caching-and-storage)
- [ Pre-generating images ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/pre-generating-images)
- [ Clearing generated images ](https://spatie.be/docs/laravel-og-image/v1/basic-usage/clearing-generated-images)

Advanced usage
--------------

- [ Customizing the page URL ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/customizing-the-page-url)
- [ Using a custom screenshot driver ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/using-a-custom-screenshot-driver)
- [ Customizing the screenshot layout ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/customizing-the-screenshot-layout)
- [ Customizing actions ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/customizing-actions)
- [ Troubleshooting ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/troubleshooting)
- [ Using a hosted solution ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/using-a-hosted-solution)
- [ Using Laravel Boost ](https://spatie.be/docs/laravel-og-image/v1/advanced-usage/using-laravel-boost)

 How it works
============

###  On this page

1. [ The big picture ](#content-the-big-picture)
2. [ Stage 1: Your page renders for the first time ](#content-stage-1-your-page-renders-for-the-first-time)
3. [ Stage 2: A crawler requests the image ](#content-stage-2-a-crawler-requests-the-image)
4. [ Stage 3: Subsequent requests ](#content-stage-3-subsequent-requests)
5. [ Performance ](#content-performance)

When you share a link on social media, platforms like Twitter, Facebook, and LinkedIn display a preview image. These are called Open Graph (OG) images. This package lets you define that image as HTML in your Blade views, and automatically screenshots it to generate the actual image file.

The big picture
-----------------------------------------------------------------------------------------------------

There are three stages to understand:

1. Your page renders: the Blade component outputs a hidden HTML template, and the middleware injects meta tags into the ``
2. A crawler fetches the image: the package generates a screenshot on the fly
3. Subsequent requests: the image is served directly from disk

Let's walk through each stage.

Stage 1: Your page renders for the first time
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

When a visitor (or a crawler) loads your page, two things happen:

1. The `` Blade component hashes the template HTML to produce a unique key (e.g. `a1b2c3d4e5f6`), stores the current page URL in cache keyed by that hash, and outputs a hidden `` tag in the page body
2. The package middleware detects the `` tag and injects `og:image`, `twitter:image`, and `twitter:card` meta tags into the ``

The HTML in the response looks like this:

```

        My Post Title

```

The `` tag is natively invisible in browsers, so visitors don't see it. The meta tags point to a route in your app (`/og-image/a1b2c3d4e5f6.jpeg`). The image doesn't exist yet, but that's fine. Crawlers will request it next.

Stage 2: A crawler requests the image
---------------------------------------------------------------------------------------------------------------------------------------------------------------------

When Twitter, Facebook, or LinkedIn sees the `og:image` meta tag, it makes a request to `https://yourapp.com/og-image/a1b2c3d4e5f6.jpeg`. The `a1b2c3d4e5f6` part is a hash generated from the HTML content of your OG image template. When you change the template content, the hash changes, producing a new URL. Here's what happens:

1. The request hits `OgImageController`, which checks if the image already exists on disk
2. If the image doesn't exist yet, the controller uses the hash from the URL to look up the original page URL from cache (stored there by the Blade component during rendering)
3. The controller tells headless Chrome to visit that page URL with `?ogimage` appended. This is a separate internal HTTP request to your app
4. `RenderOgImageMiddleware` detects the `?ogimage` parameter and replaces the response with a minimal HTML page: just your page's `` (preserving all CSS, fonts, and Vite assets) and the template content, displayed at 1200x630 pixels
5. Chrome takes a screenshot of that page and saves it to the configured disk (default: `public`)
6. The controller serves the image back to the crawler. For local disks, the image is returned directly with `Cache-Control` headers. For remote disks (S3, etc.), a 301 redirect is issued to the storage URL

Because the screenshot uses your actual page's ``, your OG image inherits all of your CSS, fonts, and Vite assets. No separate stylesheet configuration needed.

Stage 3: Subsequent requests
------------------------------------------------------------------------------------------------------------------------------------------

Once the image exists on disk, subsequent requests to `/og-image/a1b2c3d4e5f6.jpeg` serve the image directly without taking another screenshot. The meta tags always use the same stable `/og-image/{hash}.{format}` URL, which makes this work well with page caching and CDNs like Cloudflare.

Performance
-----------------------------------------------------------------------------------------

The `/og-image/` route is designed to be fast and CDN-friendly:

- The route runs without any middleware (no sessions, CSRF, or cookies)
- Images are served directly with `Cache-Control` headers, so CDNs cache the response
- The meta tag URL is stable and content-hashed, so it works correctly with page caching (Cloudflare, Varnish, etc.)
- The component skips redundant cache writes when the hash is already known
