Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Opengraph Laravel Package

shweshi/opengraph

shweshi/opengraph is a Laravel package for extracting Open Graph metadata from any URL through a simple facade. Quickly retrieve titles, descriptions, images, and other OG tags to build rich link previews, social sharing cards, and SEO-friendly content.

View on GitHub
Deep Wiki
Context7

Getting Started

shweshi/open-graph is a thin Laravel wrapper around a standalone Open Graph scraper: you hand it a URL, it fetches the page and returns an array of og:* metadata (title, description, image, url, type, site_name, and whatever else the page declares).

Minimal steps

  1. composer require shweshi/open-graph — the service provider and OpenGraph facade are auto-discovered, no manual registration on modern Laravel. v1.2.1 bumps the constraint for Laravel 13, so both Laravel 12 and 13 installs are covered; no API changes came with it.
  2. Publish and read the config before writing code: php artisan vendor:publish --provider="Shweshi\OpenGraph\OpenGraphServiceProvider" The published config/opengraph.php is small — treat the published file as the source of truth for user-agent/timeout keys rather than copying values from blog posts.
  3. First call:
use Shweshi\OpenGraph\Facades\OpenGraph;

$meta = OpenGraph::fetch('https://example.com/article');

$meta['title'] ?? null;
$meta['image'] ?? null;
  1. Your first real use case is almost always a link preview: a URL input in a form, a "fetch preview" button, and a stored card (title/description/image) rendered later. Build that end-to-end once — it exercises the caching, validation, and failure paths you will need everywhere else.

Look first at the published config and the return shape from one or two real URLs (one big site, one obscure blog) before designing anything around it.

Implementation Patterns

Wrap it, don't sprinkle it. Inject the package behind your own service so swapping scrapers later is a one-file change:

final class LinkPreviewFetcher
{
    public function fetch(string $url): ?LinkPreview
    {
        try {
            $meta = OpenGraph::fetch($url);
        } catch (\Throwable $e) {
            report($e);
            return null;
        }

        return LinkPreview::fromArray($url, $meta);
    }
}

Cache aggressively, keyed by URL. Scraping is a slow outbound HTTP call; the metadata for a given URL barely changes. Cache::remember('og:'.sha1($url), now()->addDay(), fn () => $fetcher->fetch($url)) is the single highest-value line of code in this integration.

Queue the fetch. A synchronous scrape inside a controller adds hundreds of milliseconds to seconds of latency and couples your request timeout to a third party's. Dispatch a job on form submit (or on model save), store the result on the record, and let the UI poll or simply render once present.

Store previews as polymorphic media-ish records. One link_previews table keyed by normalized URL (or a morph relation) lets multiple models share cached previews and gives you a place to record fetch status, fetched_at, and failure count.

Refresh stale previews on a schedule. A nightly job re-fetching previews older than N days keeps images and titles current without putting load on the request path.

Fall back gracefully. Many pages have partial or missing OG tags. Layer a fallback: og:title → <title> → hostname; og:image → twitter:image → placeholder. If you need that fallback, parse the HTML yourself with DOMDocument in the same job rather than adding a second scraper package.

Validate before fetching. Allow only http/https, reject credentials in the URL, and block hosts resolving to private/loopback ranges — this endpoint lets a user make your server fetch arbitrary URLs. Combine with a RateLimiter per user so nobody uses you as a scraping proxy.

Render with a Blade component. A <x-link-preview :preview="$preview" /> keeps image-ratio, rel="noopener", and lazy-loading concerns out of your controllers.

Gotchas and Tips

  • Http::fake() will not intercept it. The core library does its own HTTP transport rather than going through Laravel's HTTP client, so tests that fake the Laravel client will still hit the network. Stub your own LinkPreviewFetcher interface in tests instead — that is another reason to wrap the package.
  • Failures surface as exceptions, not empty arrays. Malformed URLs, DNS failures, non-200 responses, and timeouts all throw. Always try/catch at the boundary and persist a "failed" state so you don't retry in a loop.
  • Empty results are not errors. A 200 page with no OG tags returns little or nothing; treat that as a distinct state (fallback content) from a fetch failure.
  • Timeouts are your problem. Set a short timeout in config and pair it with a queue worker timeout; a hung scrape inside a web request will burn a PHP-FPM worker.
  • SSRF is the real risk. Never pass a raw user URL to fetch() without scheme and IP-range validation, and consider fetching only from a queue with egress restrictions.
  • Relative og:image values exist in the wild. The package does not reliably resolve them to absolute URLs — normalize against the page URL before storing.
  • Image URLs rot. Hotlinking someone's og:image is fine for a mock, not for production; mirror to your own storage when the preview is durable.
  • Upgrading to v1.2.1 is low-risk. It is a dependency-constraint change for Laravel 13 with no code or API changes — but run composer update shweshi/open-graph --with-dependencies and check for a Symfony/Illuminate HTTP constraint conflict if you were previously pinned to Laravel 12. Lockfile churn, not behavior changes, is the thing to review in the diff.
  • Extension points are limited by design. There is no driver/parser abstraction to hook; customization means post-processing the returned array or forking. If you need bespoke extraction (JSON-LD, Twitter cards, microdata), do it in your own layer rather than patching the vendor package.
  • Pin and test against real URLs. Because the output shape follows whatever the remote page declares, keep a handful of fixture URLs in a manual smoke test — regression here usually means "that site changed its tags," not "the package broke."
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo