Nuxt 3 @nuxt/image custom provider with server-side URL signing - how to avoid hydration mismatch and double fetches?
12:19 18 Dec 2025

I'm building a Nuxt 3 application using @nuxt/image with a custom provider for imgproxy. Imgproxy requires URLs to be signed using HMAC-SHA256 with a secret key and salt.

The Problem

I need to sign image URLs server-side because:

  1. The crypto module doesn't work in the browser
  2. The signing key/salt must stay secret (not exposed to client)
  3. Allow only the server to process images with imgproxy (no public endpoint)

However, every approach I've tried has significant drawbacks.

My Setup

Custom imgproxy provider (~/providers/imgproxy.ts):

import { createHmac } from "crypto";
import { defineProvider } from "@nuxt/image/runtime";

export default defineProvider<{ baseURL: string; key?: string; salt?: string }>({
  getImage(src, { modifiers = {}, baseURL, key, salt }) {
    // Sign URL using HMAC-SHA256
    const signedPath = generateSignedPath(src, modifiers, key, salt);
    return { url: `${baseURL}${signedPath}` };
  },
});

Image component uses useImage() from @nuxt/image which calls the provider.

Approaches Tried

  1. Server Component (Image.server.vue) Made the Image component a server component so signing only happens on server. Problem: Nuxt Islands fetch their HTML payload during hydration (/__nuxt_island/...), which causes the browser to parse the HTML again and fetch every image twice.
  2. Redirect API Endpoint Provider returns /api/imgproxy-sign?src=..., API signs the URL and returns 302 redirect. Problem: Hydration mismatch warning because server renders /api/imgproxy-sign?... but client expects the same (or different) URL. Also, the redirect is visible in the network tab and adds latency.

Requirements

  • Sign URLs on server only (can't use crypto in browser)
  • Keep secrets (key/salt) on server
  • No hydration mismatch warnings
  • Images should only be fetched once (no duplicate requests)
  • Ideally no redirect - direct signed URLs in HTML
  • Works for both SSR initial load and client-side navigation

Question

Is there a way to use @nuxt/image with server-side URL signing that:

  • Produces the same URL on server and client (no hydration mismatch)
  • Doesn't cause double image fetches
  • Keeps secrets on the server

I'm open to alternative architectures. Perhaps there's a Nuxt pattern I'm missing, or a different way to structure this that I haven't considered?

Environment:

  • Nuxt 3.x
  • @nuxt/image latest
  • imgproxy (self-hosted)
vue.js image-processing nuxt.js server-side-rendering nuxt3.js