1+ import { createHash } from "node:crypto" ;
12import { IncomingMessage , ServerResponse } from "node:http" ;
23import https from "node:https" ;
34import path from "node:path" ;
@@ -20,7 +21,7 @@ import {
2021// @ts -ignore
2122import type { NextUrlWithParsedQuery } from "next/dist/server/request-meta" ;
2223
23- import { loadConfig } from "./config/util.js" ;
24+ import { loadBuildId , loadConfig } from "./config/util.js" ;
2425import { awsLogger , debug , error } from "./logger.js" ;
2526import { optimizeImage } from "./plugins/image-optimization.js" ;
2627import { setNodeEnv } from "./util.js" ;
@@ -33,6 +34,7 @@ const s3Client = new S3Client({ logger: awsLogger });
3334setNodeEnv ( ) ;
3435const nextDir = path . join ( __dirname , ".next" ) ;
3536const config = loadConfig ( nextDir ) ;
37+ const buildId = loadBuildId ( nextDir ) ;
3638const nextConfig = {
3739 ...defaultConfig ,
3840 images : {
@@ -64,14 +66,24 @@ export async function handler(
6466 headers ,
6567 queryString === null ? undefined : queryString ,
6668 ) ;
69+ let etag : string | undefined ;
70+ // We don't cache any images, so in order to be able to return 304 responses, we compute an ETag from what is assumed to be static
71+ if ( process . env . OPENNEXT_STATIC_ETAG ) {
72+ etag = computeEtag ( imageParams ) ;
73+ }
74+ if ( etag && headers [ "if-none-match" ] === etag ) {
75+ return {
76+ statusCode : 304 ,
77+ } ;
78+ }
6779 const result = await optimizeImage (
6880 headers ,
6981 imageParams ,
7082 nextConfig ,
7183 downloadHandler ,
7284 ) ;
7385
74- return buildSuccessResponse ( result ) ;
86+ return buildSuccessResponse ( result , etag ) ;
7587 } catch ( e : any ) {
7688 return buildFailureResponse ( e ) ;
7789 }
@@ -115,16 +127,38 @@ function validateImageParams(
115127 return imageParams ;
116128}
117129
118- function buildSuccessResponse ( result : any ) {
130+ function computeEtag ( imageParams : {
131+ href : string ;
132+ width : number ;
133+ quality : number ;
134+ } ) {
135+ return createHash ( "sha1" )
136+ . update (
137+ JSON . stringify ( {
138+ href : imageParams . href ,
139+ width : imageParams . width ,
140+ quality : imageParams . quality ,
141+ buildId,
142+ } ) ,
143+ )
144+ . digest ( "base64" ) ;
145+ }
146+
147+ function buildSuccessResponse ( result : any , etag ?: string ) {
148+ const headers : Record < string , string > = {
149+ Vary : "Accept" ,
150+ "Content-Type" : result . contentType ,
151+ "Cache-Control" : `public,max-age=${ result . maxAge } ,immutable` ,
152+ } ;
153+ if ( etag ) {
154+ headers [ "ETag" ] = etag ;
155+ }
156+
119157 return {
120158 statusCode : 200 ,
121159 body : result . buffer . toString ( "base64" ) ,
122160 isBase64Encoded : true ,
123- headers : {
124- Vary : "Accept" ,
125- "Cache-Control" : `public,max-age=${ result . maxAge } ,immutable` ,
126- "Content-Type" : result . contentType ,
127- } ,
161+ headers,
128162 } ;
129163}
130164
0 commit comments