Caching and compression are your secret weapons against a surprisingly expensive Vercel bill.
Let’s see this in action. Imagine a simple Next.js app serving a static image.
// pages/image.js
import Image from 'next/image';
function MyImagePage() {
return (
<div>
<h1>My Awesome Image</h1>
<Image
src="/large-image.jpg"
alt="A very large image"
width={1920}
height={1080}
/>
</div>
);
}
export default MyImagePage;
Without any optimization, if a user requests pages/image.js, Vercel serves the HTML, and then the browser requests /large-image.jpg. If that image is 5MB, that’s 5MB of bandwidth. If 1000 users view it, that’s 5GB just for that one image.
The problem Vercel solves is the cost and performance hit of repeatedly sending the same large assets over the network. Every byte transferred costs money and time. The core idea is to keep frequently accessed data closer to the user or in a format that takes up less space.
Here’s how Vercel’s built-in caching and compression work under the hood, and how you leverage them:
1. Vercel’s Edge Network Caching:
Vercel deploys your application to its global Edge Network. When a user requests a page or asset, it’s served from the nearest edge location. This is powerful. For static assets (like images, CSS, JS files), Vercel caches them aggressively by default.
- What it solves: Users in different geographic locations get the same fast experience because the asset is served from a nearby server, not from a single origin.
- How it works: Vercel uses a Content Delivery Network (CDN). When the first user requests
/large-image.jpg, Vercel’s CDN fetches it from your origin (your Git repository or a specified origin server) and caches it at edge locations worldwide. Subsequent requests from users near those locations are served directly from the cache, bypassing the origin. - Your control: You don’t directly control this cache’s expiration for static assets served by Vercel. It’s optimized for speed and availability. The key is ensuring your assets are static and immutable. If you update
large-image.jpgand deploy, Vercel will invalidate the old cache entry and serve the new version.
2. Next.js Image Optimization:
For dynamic image sources or images that need resizing, Next.js provides built-in Image Optimization. Vercel automatically handles this optimization when you use the next/image component.
- What it solves: Prevents serving unnecessarily large image files. It automatically resizes, optimizes (e.g., WebP format), and caches images.
- How it works: When you use
<Image src="/large-image.jpg" width={800} height={600} />, Vercel’s Image Optimization service intercepts the request. It resizeslarge-image.jpgto 800x600, converts it to an efficient format like WebP (if the browser supports it), and serves it. This optimized image is then cached by Vercel’s CDN based on its URL (which includes the original path and requested dimensions). - Your control:
next/imagecomponent: Always use it for images that aren’t perfectly sized for their display.next.config.js: You can configure image optimization settings, though defaults are usually good. For example, to disable specific formats or change quality:// next.config.js module.exports = { images: { domains: ['your-image-domain.com'], // if images are from external domains formats: ['image/avif', 'image/webp'], // default, but shows control quality: 75, // default is 75, can go lower for more savings }, };unoptimizedprop: For truly static images where you don’t want optimization (e.g., SVGs you want to keep as-is, or images you’ve manually optimized), useunoptimized:<Image src="/my-static.svg" width={50} height={50} unoptimized />
3. HTTP Compression (Gzip/Brotli):
Vercel automatically compresses text-based assets (HTML, CSS, JavaScript, JSON, SVG) using Gzip and Brotli.
- What it solves: Reduces the size of text files transferred over the network, leading to faster load times and lower bandwidth consumption.
- How it works: When a user’s browser requests a file, it sends an
Accept-Encodingheader (e.g.,Accept-Encoding: gzip, deflate, br). Vercel’s servers detect this, compress the file on-the-fly using the best available method (Brotli is generally more efficient than Gzip), and send it back with aContent-Encodingheader (e.g.,Content-Encoding: br). The browser then decompresses it. - Your control: This is enabled by default and requires no configuration from you. The key is ensuring your assets are text-based. Binary files like JPG, PNG, MP4 are not compressed this way.
4. Browser Caching Headers:
While Vercel’s CDN handles caching at the edge, you can also instruct the user’s browser on how long to cache assets using Cache-Control headers. Vercel respects these.
- What it solves: Tells the user’s browser to store a copy of the asset locally, so subsequent requests for the same asset don’t hit the network at all.
- How it works: When Vercel serves an asset, it includes
Cache-Controlheaders in the HTTP response. For example,Cache-Control: public, max-age=31536000, immutable. This tells the browser to cache the asset for one year (31536000seconds) and that it’s immutable (meaning its content won’t change). - Your control: Vercel sets sensible defaults for static assets. You can customize these using a
vercel.jsonfile.
This configuration applies a long// vercel.json { "headers": [ { "source": "/static/(.*)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ] }max-ageto any file in a/static/directory.
The most surprising thing about Vercel’s caching is how it leverages immutable cache keys for assets. When you deploy a new version of your code, Vercel generates new, unique content hashes for your static assets (like _next/static/css/styles.a1b2c3d4.css). This means the URL itself changes. Because the URL is different, the CDN and the browser treat it as a brand new asset, ensuring users always get the latest code while still benefiting from long cache times for each specific version. This strategy eliminates stale content issues that plague simpler caching setups.
When you optimize images with next/image and Vercel’s infrastructure, the generated URLs often include hashes or other identifiers that make them unique. This allows Vercel to cache different versions of the same conceptual image (e.g., different sizes or formats) independently.
After you’ve implemented image optimization and ensured your static assets are served with appropriate cache headers, the next thing you’ll likely encounter is managing the cache invalidation for dynamic API routes or server-rendered pages.