---
title: Encrypting parameters
description: Learn how to encrypt parameters so that only certain values can be passed to generate your image.
url: /kb/guide/encrypting-parameters
canonical_url: "https://vercel.com/kb/guide/encrypting-parameters"
published: 2025-11-04
last_updated: 2025-11-11
authors: DX Team
related:
  - /docs/og-image-generation
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---
<!-- docsgraph:related -->
## Related pages

> **For AI agents:** Follow these links to understand how this page connects to the rest of the Vercel ecosystem. For the full cross-link map (inbound, outbound, prerequisites, and semantic neighbors), see the .graph.md link below.

- [Next.js](https://vercel.com/docs/frameworks/full-stack/nextjs?from=related) — Vercel is the native Next.js platform, designed to enhance the Next.js experience.
- [Metadata and OG images](https://nextjs.org/docs/app/getting-started/metadata-and-og-images?from=related) — Learn how to add metadata to your pages and create dynamic OG images.
- [Using an SVG image in your OG image](https://vercel.com/kb/guide/using-svg-image?from=related) — Learn how to use SVG embedded content to generate your OG images.
- [Get Started with BotID](https://vercel.com/docs/botid/get-started?from=related) — Step-by-step guide to setting up BotID protection in your Vercel project
- [@vercel/og](https://vercel.com/docs/og-image-generation/og-image-api?from=related) — This reference provides information on how the @vercel/og package works on Vercel.
- [Using emoji in your OG image](https://vercel.com/kb/guide/using-emoji-in-image?from=related) — Learn how to use emojis to generate an OG image.
- [Client Uploads](https://vercel.com/docs/vercel-blob/client-upload?from=related) — Learn how to upload files larger than 4.5 MB directly from the browser to Vercel Blob
- [Using Tailwind CSS with your OG Image](https://vercel.com/kb/guide/using-tailwind?from=related) — Learn how to use Tailwind CSS to style your OG images.
- [Using an external image as OG image](https://vercel.com/kb/guide/using-an-external-dynamic-image?from=related) — Learn how to pass the username as a URL parameter to pull an external profile image for the image generation.
- [Using dynamic text as your OG Image](https://vercel.com/kb/guide/dynamic-text-as-image?from=related) — Learn how to pass the image title as a URL parameter.

Full cross-link map for this page: [/kb/guide/encrypting-parameters.graph.md](/kb/guide/encrypting-parameters.graph.md)
<!-- /docsgraph:related -->


You can use the following code sample to explore using parameters and different content types with [`next/og`](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/opengraph-image#generate-images-using-code-js-ts-tsx). To learn more about OG Image Generation, see [Open Graph Image Generation](https://vercel.com/docs/og-image-generation).

This is the directory structure for these files:

```plaintext
app
├── api
│   └── encrypted
│       └── route.tsx
└── encrypted
    └── [id]
        └── page.tsx
```
```ts
import { ImageResponse } from 'next/og';
// App router includes @vercel/og.
// No need to install it.

const key = crypto.subtle.importKey(
  'raw',
  new TextEncoder().encode('my_secret'),
  { name: 'HMAC', hash: { name: 'SHA-256' } },
  false,
  ['sign'],
);

function toHex(arrayBuffer: ArrayBuffer) {
  return Array.prototype.map
    .call(new Uint8Array(arrayBuffer), (n) => n.toString(16).padStart(2, '0'))
    .join('');
}

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);

  const id = searchParams.get('id');
  const token = searchParams.get('token');

  const verifyToken = toHex(
    await crypto.subtle.sign(
      'HMAC',
      await key,
      new TextEncoder().encode(JSON.stringify({ id })),
    ),
  );

  if (token !== verifyToken) {
    return new Response('Invalid token.', { status: 401 });
  }

  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          fontSize: 40,
          color: 'black',
          background: 'white',
          width: '100%',
          height: '100%',
          padding: '50px 200px',
          textAlign: 'center',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        <h1>Card generated, id={id}.</h1>
      </div>
    ),
    {
      width: 1200,
      height: 630,
    },
  );
}
```
```js
import { ImageResponse } from 'next/og';
// App router includes @vercel/og.
// No need to install it.

const key = crypto.subtle.importKey(
  'raw',
  new TextEncoder().encode('my_secret'),
  { name: 'HMAC', hash: { name: 'SHA-256' } },
  false,
  ['sign'],
);

function toHex(arrayBuffer) {
  return Array.prototype.map
    .call(new Uint8Array(arrayBuffer), (n) => n.toString(16).padStart(2, '0'))
    .join('');
}

export async function GET(request) {
  const { searchParams } = new URL(request.url);

  const id = searchParams.get('id');
  const token = searchParams.get('token');

  const verifyToken = toHex(
    await crypto.subtle.sign(
      'HMAC',
      await key,
      new TextEncoder().encode(JSON.stringify({ id })),
    ),
  );

  if (token !== verifyToken) {
    return new Response('Invalid token.', { status: 401 });
  }

  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          fontSize: 40,
          color: 'black',
          background: 'white',
          width: '100%',
          height: '100%',
          padding: '50px 200px',
          textAlign: 'center',
          justifyContent: 'center',
          alignItems: 'center',
        }}
      >
        <h1>Card generated, id={id}.</h1>
      </div>
    ),
    {
      width: 1200,
      height: 630,
    },
  );
}
```

> If you're not using a framework, you must either add `"type": "module"` to your `package.json` or change your JavaScript Functions' file extensions from `.js` to `.mjs`

Then, you need to create a frontend component that can take an `id` query parameter, which will be passed to the API route you created above.

Create the dynamic route `[id]/page` under `/app/encrypted` and paste the following code:

```ts
// This page generates the token to prevent generating OG images with random parameters (`id`).
import { createHmac } from 'node:crypto';

function getToken(id: string): string {
  const hmac = createHmac('sha256', 'my_secret');
  hmac.update(JSON.stringify({ id: id }));
  const token = hmac.digest('hex');
  return token;
}

interface PageParams {
  params: {
    id: string;
  };
}

export default function Page({ params }: PageParams) {
  console.log(params);
  const { id } = params;
  const token = getToken(id);

  return (
    <div>
      <h1>Encrypted Open Graph Image.</h1>
      <p>Only /a, /b, /c with correct tokens are accessible:</p>
      <a
        href={`/api/encrypted?id=${id}&token=${token}`}
        target="_blank"
        rel="noreferrer"
      >
        <code>
          /api/encrypted?id={id}&token={token}
        </code>
      </a>
    </div>
  );
}
```
```js
// This page generates the token to prevent generating OG images with random parameters (`id`).
import { createHmac } from 'node:crypto';

function getToken(id) {
  const hmac = createHmac('sha256', 'my_secret');
  hmac.update(JSON.stringify({ id: id }));
  const token = hmac.digest('hex');
  return token;
}

export default function Page({ params }) {
  console.log(params);
  const { id } = params;
  const token = getToken(id);

  return (
    <div>
      <h1>Encrypted Open Graph Image.</h1>
      <p>Only /a, /b, /c with correct tokens are accessible:</p>
      <a
        href={`/api/encrypted?id=${id}&token=${token}`}
        target="_blank"
        rel="noreferrer"
      >
        <code>
          /api/encrypted?id={id}&token={token}
        </code>
      </a>
    </div>
  );
}
```

> If you're not using a framework, you must either add `"type": "module"` to your `package.json` or change your JavaScript Functions' file extensions from `.js` to `.mjs`

Run your project locally and browse to `http://localhost/encrypted/a`(`b` or `c` will also work).

Click on the generated link to be directed to the generated image.

In your actual implementation, you will use the code in `/app/encrypted/[id]/page.tsx` with a page to create your post html that will look like this. ## More resources - [Consume the OG route](/docs/og-image-generation#consume-the-og-route)
  
- [Getting started with OG image](/docs/og-image-generation#usage)