TrademarkTrademark
Docs
Ctrl k
Search docs...
Utils

handleOGProxy

Updated: October 10, 2026
View on GitHub

The handleOGProxy function is a server-side utility that proxies external images through your server. This is essential for displaying Open Graph images from external sources without CORS restrictions.

Features

  • Proxies images from external URLs
  • Passes raster image types only by default (SVG is opt-in), with nosniff and a sandboxing CSP
  • Size limit and timeout; a larger image is refused, not truncated
  • Configurable cache control headers
  • Custom user agent support
  • Only requests public addresses: private, loopback, link-local and metadata addresses are refused in every encoding, every redirect is checked again, and the connection goes to the address that was checked
  • Domain allowlist and blocklist, plus an isAllowed hook for your own checks
  • Requires Node.js runtime (resolves and checks hostnames)
  • Handles errors gracefully

Basic usage

Create an API route that uses the utility:

Then use the proxy in your client:

With options

Customize cache duration and user agent:

Why use a proxy?

External images often have CORS restrictions that prevent direct usage in web applications. By proxying images through your server:

  • Bypass CORS: Your server fetches the image and serves it from your domain
  • Caching: Reduce external requests with configurable cache headers
  • Privacy: Hide the original image URL from client-side code
  • Reliability: Handle external failures gracefully

API Reference

Type
Default
request
Request—
options
OGProxyOptions—
options.userAgent
User agent string for the fetch request.
string'Mozilla/5.0 (compatible; OG-Proxy/1.0)'
options.cacheMaxAge
Cache duration in seconds (1 hour).
number3600
options.cacheControl
Full Cache-Control for successful responses; overrides cacheMaxAge.
string—
options.timeout
Milliseconds for the whole exchange: DNS, connection, redirects and body.
number10000
options.maxBytes
Largest image, after decompression.
number5 MiB
options.allowedContentTypes
Image types passed through; image/* matches any image type.
readonly string[]DEFAULT_OG_IMAGE_TYPES
options.maxRedirects
Redirects followed, each checked like the first URL.
number3
options.allowedDomains
When set, only these domains (and their subdomains) are permitted.
string[]—
options.blockedDomains
These domains (and their subdomains) are refused.
string[]—
options.isAllowed
Called for every URL and redirect target after the built-in checks; return false to refuse.
(url: URL) => boolean | Promise<boolean>—
options.allowHttp
Also fetch plain http: URLs.
booleanfalse
options.sameOrigin
Refuse requests whose Origin or Referer names another host.
booleanfalse

Response

The function returns the image binary data with appropriate headers:

Error handling

The function returns appropriate error responses:

  • 400: Missing URL parameter or URL failed validation (blocked domain, private IP, etc.)
  • 413: The image is larger than maxBytes
  • 415: Not an allowed image type
  • 502: The upstream failed (too many redirects, a dropped connection)
  • 504: The upstream did not finish within timeout
  • 500: Not running on the Node.js runtime
  • Original status: If the external server returns an error, that status is forwarded

Use with useOgData hook

The useOgData hook automatically uses the proxy for external images:

Security considerations

Both handleOGFetch and handleOGProxy validate URLs server-side before fetching:

  • Only HTTPS URLs are allowed (plain HTTP with allowHttp)
  • Non-public addresses are blocked in every form the URL parser accepts: private, loopback, link-local, unique-local, carrier-grade NAT, multicast, cloud metadata, and IPv4 inside IPv6
  • Internal hostnames (localhost, *.local, *.internal, single-label names) are blocked
  • Every address a hostname resolves to must be public, and the connection goes to the address that was checked, so a DNS answer that changes in between has nothing to slip through
  • Embedded credentials in URLs are rejected
  • Redirect chains are followed by hand and every hop is checked again (up to 3 redirects)

Use allowedDomains to restrict fetching to trusted hosts in production:

Consider also adding rate limiting at the route level to prevent abuse.

Edit this page on GitHub
TrademarkTrademark
Built with curiosity by Lorant One and the Design Engineers Club