diff --git a/spec/server_test.js b/spec/server_test.js
index 73fcb48c14986e33d77699094266adbd948023e6..cbf20ea34810259e4e15da4b5c6a05a646076004 100644
--- a/spec/server_test.js
+++ b/spec/server_test.js
@@ -1,5 +1,5 @@
 import request from 'supertest'
-import { generateSimpleViteManifest, mockApp, mockConfig, mockFetch, mockRedis } from './util.js'
+import { brotliParser, generateSimpleViteManifest, mockApp, mockConfig, mockFetch, mockRedis } from './util.js'
 import { expect } from 'chai'
 import * as td from 'testdouble'
 import RedisMock from 'ioredis-mock'
@@ -29,6 +29,19 @@ describe('UI Middleware', function () {
   it('fetches manifest data', async function () {
     const response = await request(app.server).get('/manifests')
     expect(response.statusCode).to.equal(200)
+    expect(response.headers['content-encoding']).to.equal('gzip')
+    expect(response.body).to.deep.equal([{ namespace: 'test', path: 'example' }])
+  })
+
+  it('fetches manifest data with brotli', async function () {
+    const response = await request(app.server)
+      .get('/manifests')
+      .set('accept-encoding', 'deflate, gzip, br')
+      .buffer(true)
+      .parse(brotliParser)
+    expect(response.statusCode).to.equal(200)
+    expect(response.headers['content-encoding']).to.equal('br')
+    expect(response.headers['content-type']).to.equal('application/json; charset=utf-8')
     expect(response.body).to.deep.equal([{ namespace: 'test', path: 'example' }])
   })
 
diff --git a/spec/util.js b/spec/util.js
index 810ca69cbec31c1ffd1b0938b1e5d23c935fac2a..dbfe0e81806e53b0f03a6764cc0c600a5c675d95 100644
--- a/spec/util.js
+++ b/spec/util.js
@@ -1,6 +1,7 @@
 import * as td from 'testdouble'
 import { register } from 'prom-client'
 import RedisMock from 'ioredis-mock'
+import zlib from 'node:zlib'
 
 export function generateSimpleViteManifest (mapping) {
   const viteManifest = {}
@@ -64,3 +65,17 @@ export async function mockApp () {
   await app.listen({ port: 0 })
   return app
 }
+
+export async function brotliParser (res, cb) {
+  const brotli = zlib.createBrotliDecompress()
+  let buffer = Buffer.from('')
+  res.pipe(brotli)
+  brotli.on('data', chunk => (buffer = Buffer.concat([buffer, chunk])))
+  brotli.on('end', () => {
+    let result = buffer.toString()
+    if (res.headers['content-type'].startsWith('application/json')) {
+      result = JSON.parse(result)
+    }
+    cb(null, result)
+  })
+}
diff --git a/src/manifests.js b/src/manifests.js
index bf014cc491f3587d32d595cde20fe2eb49eb0db0..14f71a0d81d0022512e6a3faccf8eafde7db90f2 100644
--- a/src/manifests.js
+++ b/src/manifests.js
@@ -2,6 +2,9 @@ import { configMap } from './configMap.js'
 import { getRedisKey, viteManifestToDeps, viteToOxManifest } from './util.js'
 import { logger } from './logger.js'
 import * as cache from './cache.js'
+import zlib from 'node:zlib'
+import { promisify } from 'node:util'
+const brotliCompress = promisify(zlib.brotliCompress)
 
 export async function fetchViteManifests () {
   // vite manifests contains a set of objects with the vite-manifests
@@ -32,9 +35,15 @@ export async function getViteManifests ({ version }) {
 }
 
 export function getOxManifests ({ version }) {
-  return cache.get(getRedisKey({ version, name: 'oxManifests' }), async () => {
+  return cache.getFile({ name: 'oxManifests', version }, async () => {
+    const headers = { 'content-type': 'application/json; charset=utf-8', 'content-encoding': 'br' }
     const viteManifests = await getViteManifests({ version })
-    return viteToOxManifest(viteManifests)
+    const oxManifests = await viteToOxManifest(viteManifests)
+    const body = await brotliCompress(JSON.stringify(oxManifests))
+    return {
+      body,
+      headers
+    }
   })
 }
 
diff --git a/src/plugins/manifests.js b/src/plugins/manifests.js
index 0f074c1394f60557bcce9eabba1a508cd41fe0c1..5676ad2674edaf0fdfeb5340e5480676a0c6b2f4 100644
--- a/src/plugins/manifests.js
+++ b/src/plugins/manifests.js
@@ -1,8 +1,11 @@
 import { getOxManifests } from '../manifests.js'
 
 export default async function manifestsPlugin (fastify, options) {
-  fastify.get('/manifests', async (req, res) => {
-    if (res.body) return
-    res.send(await getOxManifests({ version: res.version }))
+  fastify.get('/manifests', async (req, reply) => {
+    if (reply.body) return
+
+    const { body, headers } = await getOxManifests({ version: reply.version })
+    reply.headers(headers)
+    reply.send(body)
   })
 }
diff --git a/src/plugins/serve-files.js b/src/plugins/serve-files.js
index 4ec1469b9c2fd6bf6f0a1dbc143d25168440db07..df9a17a1165c99a6a2bb04fe4bf25d4e44a67ba5 100644
--- a/src/plugins/serve-files.js
+++ b/src/plugins/serve-files.js
@@ -11,9 +11,7 @@ export default async function serveFilePlugin (fastify, options) {
       const path = url === '/' ? '/index.html' : url
       const { body, headers } = await getFile({ version, path })
 
-      Object.entries(headers).forEach(([key, value]) => {
-        reply.header(key, value)
-      })
+      reply.headers(headers)
       reply.send(body)
     } catch (err) {
       const errors = err.errors || [err]