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.
- 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
Create an API route that uses the utility:
Then use the proxy in your client:
Customize cache duration and user agent:
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
The function returns the image binary data with appropriate headers:
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
The useOgData hook automatically uses the proxy for external images:
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.