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
Getting started
Section titled “Getting started”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.
Examples
Section titled “Examples”Replacing content in the response
Section titled “Replacing content in the response”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(); }}Injecting JavaScript into the response
Section titled “Injecting JavaScript into the response”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(); }}Manipulating response headers
Section titled “Manipulating response headers”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(); }}Applying a content filter to a route
Section titled “Applying a content filter to a route”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).