---
title: "Embedding OG Images"
description: "Render another page's OG image in an <img> tag without reconstructing its URL."
canonical_url: "https://nuxtseo.com/docs/og-image/guides/resolve-og-images"
last_updated: "2026-09-05T15:41:53.962Z"
---

The generated URL for an OG image depends on the options passed to `defineOgImage()`{lang="ts"}, including the component hash and any props. These URLs change when you edit a template, so hardcoding them in a blog index or listing page breaks on every deploy.

The `/_og/r/` endpoint solves this. Point it at any page path and it fetches the page, reads the `og:image` meta tag, and redirects (302) to the current image URL.

```vue [pages/blog/index.vue]
<template>
  <article v-for="post in posts" :key="post.path">
    <NuxtLink :to="post.path">
      <img :src="`/_og/r${post.path}`" :alt="post.title">
      <h3>{{ post.title }}</h3>
    </NuxtLink>
  </article>
</template>
```

Browsers follow the 302 transparently, so the image renders exactly as it would on the original page.

## Using The Current Page's URL

If you're showing the OG image on the same page that declares it, skip the resolver entirely. `defineOgImage()`{lang="ts"} returns the generated URLs:

```vue
<script lang="ts" setup>
const [ogImageUrl] = defineOgImage('NuxtSeo', { title: 'Hello' })
</script>

<template>
  <img :src="ogImageUrl" alt="Preview">
</template>
```

The return type is `string[]`{lang="ts"} with one URL per variant. See [`defineOgImage()`{lang="ts"}](/docs/og-image/api/define-og-image) for details.

## Selecting `twitter:image`

By default the resolver follows `og:image`. Pass `_og_key=twitter`{lang="html"} to follow `twitter:image` instead:

```html
<img src="/_og/r/blog/hello-world?_og_key=twitter">
```

## Dynamic Pages

Query parameters on the resolver URL (other than `_og_*`) are forwarded to the target page fetch, so dynamic routes render against the right props:

```html
<img src="/_og/r/products?id=42">
```

This fetches `/products?id=42`, then redirects to whatever `og:image` that page produced.

## Image Extension Suffix

Some tools refuse to treat a URL as an image unless it ends in an image extension. The resolver strips a trailing `.png`, `.jpg`, `.jpeg`, `.webp`, or `.svg` before resolving:

```html
<img src="/_og/r/blog/hello-world.png">
```

The extension is cosmetic. The redirect target is whatever the page declares.

## Caching

The resolver emits `Cache-Control: public, max-age=<ttl>, s-maxage=<ttl>, immutable` using your module's `cacheMaxAgeSeconds`, so CDNs cache the redirect without revalidation for the configured TTL.

To override the default, set your own headers via route rules:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  routeRules: {
    '/_og/r/**': { headers: { 'cache-control': 'public, max-age=3600' } }
  }
})
```

## Security

The resolver respects the same settings as the image handlers. See the [security guide](/docs/og-image/guides/security) for configuration.

| Protection                               | Behavior                                 |
| ---------------------------------------- | ---------------------------------------- |
| `security.restrictRuntimeImagesToOrigin` | Blocks requests from unknown hosts       |
| `security.maxQueryParamSize`             | Caps the forwarded query string          |
| Internal route guard                     | Rejects paths starting with `/_`         |
| Scheme and protocol-relative guard       | Rejects `//evil.com` and `http://` paths |
| Path length cap                          | Rejects paths over 2048 characters       |

The redirect target is whatever the page's own `og:image` declares. The resolver does not validate the destination, so its trust boundary is the set of pages on your site that call `defineOgImage()`{lang="ts"} or set `og:image` via `useSeoMeta()`{lang="ts"}.