Skip to content

Content filters

Content filters are a way to manipulate the response before it is sent to the client. This includes both the response body and headers.

This can be useful for a variety of purposes such as:

  • Injecting additional content or scripts into the response
  • Using regex to replace content in the response
  • Blocking access to certain content
  • Implementing security measures, such as preventing markup containing reference to a compromised third-party service
  • Programmatically adding or modifying response headers

To begin, create a new content filter in the QuantCDN dashboard from the “Compute > Content filters” section.

The code editor will open in the right-hand pane. By default, the editor will contain a basic example of a content filter.

The response body is manipulated as it is streamed to the client, so you can alter the response in real-time on chunks of data. Response headers can also be modified before they are sent to the client.

The following example shows how to replace all instances of the word “example” with the word “test”.

class contentFilter extends TransformStream {
constructor(event, responseHeaders) {
super({
transform: (chunk, controller) => {
const chunkStr = this.textDecoder.decode(chunk);
const transformedChunkStr = chunkStr.replace(/example/g, 'test');
const outputBytes = this.textEncoder.encode(transformedChunkStr)
controller.enqueue(outputBytes);
},
});
this.textEncoder = new TextEncoder();
this.textDecoder = new TextDecoder();
}
}

The following example shows how to inject a custom script into the response, before the closing </body> tag.

class contentFilter extends TransformStream {
constructor(event, responseHeaders) {
const clientCountryCode = event.request.headers.get('client-geo-country') ?? 'XX'
super({
transform: (chunk, controller) => {
const chunkStr = this.textDecoder.decode(chunk);
// Example alert script with the client country code
const exampleScript = `<script>alert('${clientCountryCode}');</script>`
const transformedChunkStr = chunkStr.replace('</body>', exampleScript + '</body>');
const outputBytes = this.textEncoder.encode(transformedChunkStr)
controller.enqueue(outputBytes);
},
});
this.textEncoder = new TextEncoder();
this.textDecoder = new TextDecoder();
}
}

Content filters can also manipulate response headers programmatically. This is powerful for implementing dynamic caching strategies, A/B testing, geo-targeting, or adding context-aware headers based on request characteristics.

class contentFilter extends TransformStream {
constructor(event, responseHeaders) {
const url = new URL(event.request.url);
const userAgent = event.request.headers.get('user-agent') || '';
const clientCountry = event.request.headers.get('client-geo-country') ?? 'XX';
const isMobile = /mobile|android|iphone/i.test(userAgent);
// Dynamic cache control based on device type and path
if (url.pathname.startsWith('/api/')) {
// API endpoints: no caching
responseHeaders.set('cache-control', 'no-store, must-revalidate');
} else if (isMobile) {
// Mobile: shorter cache for frequently changing layouts
responseHeaders.set('cache-control', 'public, max-age=300');
} else {
// Desktop: longer cache
responseHeaders.set('cache-control', 'public, max-age=3600');
}
// Add custom headers for analytics/debugging
responseHeaders.set('x-device-type', isMobile ? 'mobile' : 'desktop');
responseHeaders.set('x-client-country', clientCountry);
// A/B testing: assign variant based on country
const variant = ['US', 'CA', 'GB'].includes(clientCountry) ? 'A' : 'B';
responseHeaders.set('x-ab-variant', variant);
let replacementValue = `Variant ${variant} for ${clientCountry} (${isMobile ? 'mobile' : 'desktop'})`;
let tokenPattern = '{{ PLACEHOLDER_TOKEN }}';
super({
transform: (chunk, controller) => {
const chunkStr = this.textDecoder.decode(chunk);
const transformedChunkStr = chunkStr.replace(tokenPattern, replacementValue);
const outputBytes = this.textEncoder.encode(transformedChunkStr)
controller.enqueue(outputBytes);
},
});
this.textEncoder = new TextEncoder();
this.textDecoder = new TextDecoder();
}
}

To apply a content filter to the route, you can add a new Page Rule to apply to specific domains or routes (or any other supported condition).