We were missing some test coverage for image optimization. This PR adds
tests for many missing image formats.
Additionally, this PR also ensures the sharp encoders match that
allowlist from detectContentType() such that future versions of sharp
that enable new encoders by default are not automatically enabled in
next/image.
Practically, there is no change to the end user because
detectContentType() runs before sharp so that is the true allowlist, but
this gives us a little more control to align the sharp allowlist as well
(since sharp can release new features in semver minor).
In a previous PR https://github.com/vercel/next.js/pull/88183, we added
`images.maximumResponseBody` but in only applied to external images.
This PR ensures the same config also applies to internal images.
Some users have been confused by the relationship of
`images.remotePatterns` and `images.maximumRedirects` so lets document
it.
This behavior is expected since you might allowlist something like
`https://github.com/styfle.png` and that can redirect to whatever it
needs to redirect to (assuming maximumRedirects > 0). For example, it
currently redirects to
`https://avatars.githubusercontent.com/u/229881?v=4` but you can imagine
in the future if the redirect was changed, you wouldn't want your app to
break.
Regardless of redirects, the final response must be an image.
---------
Co-authored-by: Zack Tanner <[email protected]>
This PR adds an LRU disk cache so that reads and writes from the Image
Optimization API will evict old entries based on the value of
`images.maximumDiskCacheSize` configuration.
The LRU ensures that cache reads bump the entry to the top so that they
don't get evicted - only the least recently used entries get evicted.
When `next start` is run, if there is an existing disk cache the we will
replay the files in order to populate the LRU and respect
`images.maximumDiskCacheSize` if it was changed.
If no configuration is provided, default to 50% available disk space.
Based on our metrics, the P99.9 for a source image is 47 MB so we can
adjust the default setting to be much lower (by changing from from 300
MB to 50 MB) in favor of reducing memory.
Release notes: https://github.com/lovell/sharp/releases/tag/v0.34.5
- Upgrade to libvips v8.17.3 for upstream bug fixes.
- Add experimental support for prebuilt Linux RISC-V 64-bit binaries.
- Support building from source with npm v12+
- Add support for BigTIFF output.
- Improve error messaging when only warnings issued.
- Simplify ICC processing when retaining input profiles.
In a previous PR https://github.com/vercel/next.js/pull/73184 the `?dpl`
query string was added to local images from static imports but not to
images without the static import.
This was a subtle change based on malte's PR review comment here:
https://github.com/vercel/next.js/pull/73184#issuecomment-2499126751
However, I can't think of reason why we should differentiate these two
since they are both related to the current deployment (as opposed to
remote images which live outside the deployment).
Furthermore, this string matching is causing microfrontends to fail to
add the `?dpl` because they have a path prefix and thus fail skew
protection.
So this PR fixes both problems by loosening the string matching to look
for src images that start with `/`.
This PR adds a two new options and sets a strict default value for each.
- `images.dangerouslyAllowLocalIP`
- `images.maximumRedirects`
### dangerouslyAllowLocalIP
In rare cases when self-hosting Next.js on a private network, you may
want to allow optimizing images from local IP addresses on the same
network.
However, this is not recommended for most users so the default is
`false`.
> [!NOTE]
> BREAKING CHANGE: This change is breaking for those who self-hosting
Next.js on a private network and want to allow optimizing images from
local IP addresses on the same network. In those cases, you can still
enable the config.
### maximumRedirects
Since are also testing redirects for local IPs, we can also reduce the
maximum number of redirects to 3 by default.
Unlike normal websites which might redirect for features like auth, its
unusual to have more than 3 redirects for an image.
In some rare cases, developers may need to increase this value or set to
`0` to disable redirects.
> [!NOTE]
> BREAKING CHANGE: This change is breaking for those who need image
optimization to follow more than 3 redirects.
We took a look at unique Next.js projects that have one or more requests
for a specific image size and here are the findings as seen from the
Image Optimization API:
- `w=16` 4.2%
- `w=32` 17.1%
- `w=48` 21.4%
- `w=64` 22.6%
- `w=96` 25.8%
- `w=128` 29.5%
- `w=256` 46.5%
- `w=384` 35.1%
- `w=640` 57.2%
Only 4.2% of Next.js projects ever made a request to a 16px width image.
This is low enough to remove from the default configuration so that all
other projects can benefit from reduced html
[srcset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset)
and fewer variations exposed from the backend API.
You might think that this means few developers are using `<Image
width={16} />` but it probably means that most displays nowadays use
devicePixelRatio 2 meaning that specifying a 16px image width will
actually fetch the 32px width to ensure it won't looking blurry on your
retina display.
BREAKING CHANGE: this is technically a breaking change but it won't
cause apps to stop working, its just 4% of them may have some requests
serve a 32px image instead of 16px. Those apps can of course opt in by
changing their `images.imageSizes` config back to allowing 16.
`next/legacy/image` was introduced in Next.js 13 as way to allow
developers to upgrade from 12 to 13 with minimal effort.
Now that its been 3 years, its time to deprecate `next/legacy/image` in
Next.js 16 so we can remove it in a future version.
This will print a warning the first time the component is used.
This was docs deprecated in Next.js 14 so we should begin warning in
Next.js 16 when `images.domains` config is used.
This will guide users to providing a strict `images.remotePatterns`
config ensuring only expected images can be optimized.
This fixes an issue where the tag was `/` but the build output was
`/index` (and vice-versa) meaning you couldn't use `revalidatePath()` in
some cases because the tag didn't match.
---------
Co-authored-by: JJ Kasper <[email protected]>
We have found many Next.js users are confused why images keep
revalidating so frequently (increasing cpu and cost).
The reason is usually that their upstream source images are missing the
`cache-control` header and thus fallback to the 60 second revalidation
default. This its not a great default since most images don't change
frequently.
This PR is a breaking change to bump the `images.minimumCacheTTL` config
from 60 (1 min) to 14400 (4 hours).
Why 4 hours? Because its long enough to take advantage of the durable
cache, but short enough that changing or deleting the upstream src image
will take effect on the same day.
For advanced use cases where images are changing frequently, developers
can change this behavior back by reducing `images.minimumCacheTTL` to a
lower value.
The Image component's existing `priority` prop is confusing since its
not obvious what it does.
In order to improve clarity, we are introducing a `preload` prop and
deprecating the `priority` prop.
We also updated the dev warning to no longer suggest using this prop for
the LCP and instead use `loading="eager"`.
## Background
We have default allowlists for the `url` (localPatterns/remotePatterns
config) and `w` (imageSizes/deviceSizes config) but we don't have a
default allowlist for `q` (currently opt-in with via qualities config).
## What's changing?
BREAKING CHANGE: This PR is a breaking change that sets the default
allowlist to `qualities: [75]`, meaning that anything other than 75 is
invalid. Since most images don't set the quality prop and therefore
default to 75, most apps will be unaffected.
However, we can be even more gracious when users upgrade because we can
coerce the value automatically to 75 (or rather any value in `qualities`
allowlist). In dev, this will print a warning explaining why the prop
was not observed. This also works if you had `qualities` configured but
removed 75, since the closet matching value in the array will be used
instead.
Add a warning to Next.js 15.5 to prepare for Next.js 16 when
`images.localPatterns` is undefined and the user attempted to optimize a
`src` image with a query string, such as
```jsx
<Image src="/api/user?id=1" width="50" height="50" />
```
As a follow up to PR https://github.com/vercel/next.js/pull/82538 this
PR adds an experimental flag to optionally skip `sharp.metadata()` and
rely only on the JS implementation for format detection.
In a previous PR https://github.com/vercel/next.js/pull/82118 we added a
fallback to `sharp().metadata()` when the magic number didn't detect the
image format in order to improve detection.
But `sharp` can be slow to read metadata, so we should avoid it if we
can.
So this PR uses the existing `image-size` package to detect the content
type in JS and only then does it fallback to the native `sharp` code if
necessary.
No tests were added since this doesn't change the behavior, only
performance.
Add support for detecting more src image formats via magic number. The
src image formats are handled as follows:
- `image/jxl` - serve as is (since safari can render it)
- `image/heic` - serve as is (since safari can render it)
- `image/jp2` - serve as is (since safari can render it)
- `application/pdf` - error (since no browser will render it)
- `image/pic` - error (since no browser will render it)
We also fallback to `sharp().metadata()` if we can't detect the magic
number to ensure correctness.
The headers were forwarded to the serverless function for "internal"
images but not "external" images.
This changes the behavior to be the same for both such that neither
receive headers.
Add a warning to Next.js 15.5 to prepare for Next.js 16 when
`images.qualities` is undefined and the user attempted to use set
`quality` prop to something other than 75.
The stale bot comment says 2 years even though its 1.5 years (or 545
days to be precise).
The exact amount of time is not as important in this message so we can
remove it to avoid drift between the message and actual stale time.
Configuring `remotePatterns` can be a hassle because you have to define
each of the parts. And if you forget a part, its a wildcard meaning it
will match anything. This is usually not desirable since most remote
images don't allow query strings.
This PR adds support for using an array of `URL` objects when defining
`images.remotePatterns`.
```js
module.exports = {
images: {
remotePatterns: [
new URL('https://res.cloudinary.com/my-account/**'),
new URL('https://s3.my-account.*.amazonaws.com/**'),
],
},
}
```
Note that wildcards must be explicit and anything missing is assumed to
no longer match.
The docs for `minimumCacheTTL` showed the default but didn't show an
example of changing it.
Based on looking at many websites and the frequency those images change,
I think 31 days is a good choice.
This PR adds support for `images.qualities` configuration which is an
allowlist of qualities that can be used with the quality prop on the
Image component.
- Depends on https://github.com/vercel/vercel/pull/12792
We typically add `search: ''` on `remotePatterns` to disallow the query
string, which is the most common case.
There were a few missing places in the docs so this PR fills in those
gaps.
These features were added in Next.js 15.x but then back-ported to 14.x
since they can impact billing.
So we should land this docs PR and then also back-port it.
I also noticed that the heading was wrong for `localPatterns`.
The `warnOnce()` function is used to warn only once time, however this
means we might be storing every possible message in memory forever (aka
memory leak) since it can grow unbounded.
This PR changes the behavior to only store only 10KB in a Least Recently
Used (LRU) cache.
The tradeoff here is that you might see the same warning twice if you
have a lot of unique warnings.
- cleanup unused code paths since `optimizedBuffer` and `upstreamBuffer`
are always defined
- refactor additional options in to `opts` object
- add `opts.silent` option to disable logs
This is easiest to review with whitespace disabled:
https://github.com/vercel/next.js/pull/73373/files?diff=split&w=1
Currently, enabling skew protection will cause new deployments to
invalidate the browser cache since the deployment id is including in the
query string as `dpl`. In many cases, such as JS and CSS, these assets
are bundled and include a content hash in the file name, so new
deployments already invalidate browser cache. However, the optimized
image cache is preserved between deploys, so enabling skew protection
breaks that expectation.
This PR removes the `dpl` query string for remote images since those
files don't live in the deployment and therefore don't need skew
protection.
- Related to https://github.com/vercel/next.js/discussions/73015
## History
In a previous PR https://github.com/vercel/next.js/pull/65846 the chroma
subsampling setting was removed for AVIF images. This caused the default
of `4:4:4` to be used which increased the image size and CPU a lot.
I considered switching back to `4:2:0` but found that reducing the
`effort: 3` actually yielded significantly better performance with only
a slight difference in size.
## Benchmark
For example, take this PNG that has a width of 1805.
```
curl -JO https://images.bookroo.com/site/landing/homepage/book-collection.png
```
Using `w=1850` and `q=75`:
```
Current: 2_778ms and 153_364 bytes
Chroma: 2_214ms and 132_927 bytes
This PR: 1_265ms and 158_862 bytes
```
Using `w=1500` and `q=75`:
```
Current: 625ms and 95_810 bytes
Chroma: 524ms and 89_500 bytes
This PR: 185ms and 99_127 bytes
```
Using `w=1200` and `q=75`:
```
Current: 506ms and 74_738 bytes
Chroma: 407ms and 68_998 bytes
This PR: 144ms and 77_955 bytes
```
For this particular image, the size will increase a bit but there are
other images I tested where the size was actually smaller. So its a big
improve in CPU usage compared to reverting to the chroma subsampling
setting.
We also are keeping AVIF smaller than WebP which is the expected
behavior.
## Learn More
[Chroma subsampling](https://en.wikipedia.org/wiki/Chroma_subsampling)
is the practice of encoding images by implementing less resolution for
[chroma](https://en.wikipedia.org/wiki/Chrominance)
[information](https://en.wikipedia.org/wiki/Information) than for
[luma](https://en.wikipedia.org/wiki/Luma_(video)) information, taking
advantage of the human visual system's lower acuity for color
differences than for luminance
Image compression deep-dive video:
https://www.youtube.com/watch?v=F1kYBnY6mwg
Sharp Options: https://sharp.pixelplumbing.com/api-output#avif
The `<Image>` component doesn't optimize SVG images since they are
vector images and can already scale up and down to any width.
Therefore, the component will try to detect SVG images based on the file
name to avoid sending them to the Image Optimization API at all. This is
because the default behavior of the Image Optimization API is to error
for SVG images due to the security implications of `<svg>` with nested
`<script>`, etc (unless `dangerouslyAllowSVG` is enabled.
However, we don't want users to reach for `dangerouslyAllowSVG` because
it is not usually what they intended. So instead, we can improve the
heuristic for detecting SVG in the `<Image>` component by ignoring the
query string.
Related to https://github.com/nodejs/nodejs.org/pull/7244
This PR adds a new example that shows a more complex use case where you
don't want browser to know about the upstream image server so instead
you can proxy through a serverless function.
Some use cases might be checking for an IP address, user agent, etc.
This PR adds missing documentation for the supported src image types.
The input format support is determined my `sharp` and can be found here:
https://sharp.pixelplumbing.com/api-constructor
This recommendation to use `priority` instead of `loading=eager` is a
little confusing.
We don't want `priority` to be overused because it can hurt performance
so we can remove this recommendation from the documentation.
TLDR:
- use `priority` for LCP like a hero image above the fold
- use `loading=eager` for other images above the folder
- use `loading=lazy` (default) for everything below the fold
- use `fetchPriority=high` if you want to load an image out of order
that its found in the document
There is a React error that suggests the user change `src=""` to
`src={null}`.
> An empty string ("") was passed to the src attribute. This may cause
the browser to download the whole page again over the network. To fix
this, either do not render the element at all or pass null to src
instead of an empty string.
Even though `next/image` treats those both the same way, a user
attempting to use `src={null}` would cause the build to crash with a
confusing error message (JS only problem since TS would catch it much
earlier since src is a required prop).
```
⨯ TypeError: Cannot read properties of null (reading 'default')
at isStaticRequire (.next/server/chunks/ssr/node_modules__pnpm_1b81c1._.js:142:16)
at isStaticImport (.next/server/chunks/ssr/node_modules__pnpm_1b81c1._.js:148:40)
at getImgProps (.next/server/chunks/ssr/node_modules__pnpm_1b81c1._.js:320:9)
at .next/server/chunks/ssr/node_modules__pnpm_1b81c1._.js:2839:82
digest: "1613180311"
```
This PR ensures that `src={null}` will print the expected error that was
added back in a previous PR:
- https://github.com/vercel/next.js/pull/38847
Since `images.localPatterns` config can be undefined, we need to support
that when emitting `images-manifest.json`.
- When `undefined`, all local images are allowed
- When `[]`, no local images are allowed
Note this is different than `remotePatterns` since it treats `undefined`
and `[]` the same (the default being no remote images are allowed).
This adds support for `images.localPatterns` config to allow specific
local images to be optimized and (more importantly) block anything that
doesn't match a pattern.
This PR adds a new feature to the existing `remotePatterns` allowlist
configuration, that enables the ability to filter on `search` (aka query
string).
The most common usage will likely be `search: ''`, which means no query
string allowed.
You can also set the exact query string such as `search: '?v=1'` which
can be useful to invalidate the cache one time for a specific version
without opening the door to any version. Note the leading question mark
to match the behavior of `new URL(url).search`.
## History
When we added `priority` prop to `next/image`, there was no
`fetchPriority` so we instead used this to preload the image in the
head.
Then when browsers added support `fetchPriority`, we automatically added
`fetchPriority=high` when `priority={true}` to signal to the browser
that this was a high priority image. This priority is added to the img
and the preload.
However, we saw cases where images are blocking critical css. Per
@gnoff:
> React currently prioritizes font preloads then high priority images,
then css in the initial page load
Due to these changes in React (aka Float), we should no longer set
`fetchPriority=high`, although the user can still manually add that prop
if needed.
The Image Optimization API currently uses `parseInt()` for `w` and `q`
query string parameters.
This doesn't handle floats as you might expect and instead coerces, for
example `parseInt('99.9') === 99`.
This PR changes the runtime to match the build time validation and only
accept integers for `w` and `q`.
This parameter was previously used for a warning message, but we no
longer print that warning since
https://github.com/vercel/next.js/pull/63321 so now we can remove the
unused parameter.
This PR sets the upstream image timeout to 7 seconds so its not
unbounded (P99 is about 3 to 4 seconds).
We also set the sharp timeout to 7 seconds (P99 is about 2 seconds
although it depends on CPU).
This means an image could take at most 14 seconds to fetch and optimize.
BREAKING CHANGE:
Using the built-in image optimization API, the URL is parsed with `new
URL()` constructor which automatically trims spaces.
However, the developer may choose a 3rd party image optimization API via
`loader` or `loaderFile` (or perhaps a deployment platform that has its
own built in loader), so we shouldn't assume the API will parse the URL
in the same way as
[WHATWG](https://url.spec.whatwg.org/#:~:text=If%20input%20contains%20any%20leading%20or%20trailing%20C0%20control%20or%20space%2C%20invalid%2DURL%2Dunit%20validation%20error.).
While we could trim on the client, its probably best to fail fast and
let the developer make a conscience decision if a trailing space should
be removed or remain (by explicitly using `%20`).
### BREAKING CHANGE
This changes the behavior of the default image `loader` so that
[`Content-Disposition`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#as_a_response_header_for_the_main_body)
header is now `attachment` for added protection since the API can serve
arbitrary remote images.
The new default value, `attachment`, forces the browser to download the
image when visiting directly. This is particularly important when
`dangerouslyAllowSVG` is true. Most users will not notice the change
since visiting pages won't behave any differently, only visiting images
directly.
Users can switch back to the old behavior by configuring `inline` in
next.config.js
```js
module.exports = {
images: {
contentDispositionType: 'inline',
},
}
The tests added in https://github.com/vercel/next.js/pull/46219 were
never correctly testing the headers because `detectContentType()` is
called first and only when we can't detect the type from the response
body do we fallback to the `Content-Type` header.
I also refactored some of the tests because the `ctx: any` type was
causing some tests to not run when testing different configuration
options.
Closes NEXT-3321
This ensures that the instrumentation hook for Node.js will run
immediately during `next start` instead of waiting for the first request
like does it `next dev`.
However, if there is a separate instrumentation hook for Edge, that will
still be lazy evaluated and wait until the first request.
Fixes#59999
Fixes NEXT-2738
This PR stabilizes an experimental feature that was added in a previous
PR https://github.com/vercel/next.js/pull/50470
It allows the user to set `deploymentId` in `next.config.js`, which is a
unique identifier for a deployment that will be included in each
request's query string or header.
This PR is easier to review with whitespace hidden:
https://github.com/vercel/next.js/pull/63198/files?w=1
Closes NEXT-2789
Previously, this error was confusing because it made it sound like the
`sizes` prop was missing. This was because the default value of `sizes`
is `100vw` so the previous code couldn't tell the different between
implicit vs explicit `100vw`.
This PR changes the code to read the input value from the `sizes` prop
and prints a better warning.
Fixes NEXT-2441
### Why
The `process/browser` lib was imported and being transformed, but
`process.browser = true` turns to `true = true` that causes the
compilation error, we need to exclude them from certain compilation
layers
### What
- Fixes https://github.com/vercel/next.js/issues/61116
Closes NEXT-2410
Closes NEXT-2328
---------
Co-authored-by: Jiachi Liu <[email protected]>
## History
Previously, we added support for `squoosh` because it was a wasm
implementation that "just worked" on all platforms when running `next
dev` for the first time. However, it was slow so we always recommended
manually installing `sharp` for production use cases running `next
build` and `next start`.
Now that [`sharp` supports
webassembly](https://sharp.pixelplumbing.com/install#webassembly), we no
longer need to maintain `squoosh`, so it can be removed. We also don't
need to make the user install sharp manually because it can be installed
under `optionalDependencies`. I left it optional in case there was some
platform that still needed to manually install the wasm variant with
`npm install --cpu=wasm32 sharp` such as codesandbox/stackblitz (I don't
believe sharp has any fallback built in yet).
Since we can guarantee `sharp`, we can also remove `get-orientation` dep
and upgrade `image-size` dep.
I also moved an [existing `sharp`
test](https://github.com/vercel/next.js/pull/56674) into its own fixture
since it was unrelated to image optimization.
## Related Issues
- Fixes https://github.com/vercel/next.js/issues/41417
- Closes https://github.com/vercel/next.js/pull/54670
- Related https://github.com/vercel/next.js/issues/54708
- Related https://github.com/vercel/next.js/issues/44804
- Related https://github.com/vercel/next.js/issues/48820
This PR is refactoring only and doesn't change the logic, therefore no
tests were added.
The improvement here is readability:
- separate functions to fetch external vs internal image (in the future,
we could improve perf of internal image routing)
- types of the arguments are more specific (rather than passing in
complete NextConfig)
> Note: Its easier to review with whitespace hidden:
https://github.com/vercel/next.js/pull/61172/files?w=1
This PR adds a warning when attempting to optimize an animated image.
```jsx
<Image src="/image.gif" />
```
The warning looks like the following:
```
The requested resource "/image.gif" is an animated image so it will not be optimized. Consider adding the "unoptimized" property to the <Image>.
```
To remove the warning, add the `unoptimized` prop.
```jsx
<Image src="/image.gif" unoptimized />
```
We don't attempt to optimized animated images because it can be very
slow (30+ seconds) and sometimes deoptimizeds (the output is larger than
the input) so its best to serve the animated image as-is.
Closes NEXT-2199