203 Non-Authoritative Information
What is HTTP 203 Non-Authoritative Information?
Sometimes when you ask a website for information, it goes through a helper compu...
Explain Like I’m 3
You asked someone for a message, but someone in the middle changed it a tiny bit before giving it to you. It’s still mostly the same, but they wanted you to know it’s a little different from the original!
Example: Your friend sends you a drawing through another friend. The friend in the middle adds a sticker before giving it to you and says ‘I added a sticker!’ So you know it’s a tiny bit different from the original.
Explain Like I’m 5
Sometimes when you ask a website for information, it goes through a helper computer called a ‘proxy’ that sits in between. Usually the proxy just passes the information along exactly as it is. But sometimes the proxy changes something - like removing bad words, adding a watermark, or fixing the format. When this happens, the proxy sends ‘203 Non-Authoritative Information’ to tell you ‘The information came from the website, but I changed it a little bit.’ That way you know it’s not exactly what the original website sent!
Example: You ask for a webpage with pictures. A school computer in the middle receives it first, blocks one inappropriate image, and then sends you the rest with a message: ‘203 - I filtered this page for safety.’ You got most of the content, but the school computer changed it slightly.
Jr. Developer
HTTP 203 Non-Authoritative Information indicates that the request was successful, but a transforming proxy modified the response from the origin server. The proxy is telling you ‘I changed something - this isn’t exactly what the origin server sent.’ Common transformations include header modifications, content filtering (malware/inappropriate content), format transcoding, privacy filtering, or adding caching/mirror information. This status is quite rare in practice. The proxy could have changed headers, removed content, or modified the body. The problem: you can’t tell what the original status code was - it might have been 200, 201, or something else. This status code is less useful than the deprecated Warning header (214 Transformation Applied) because 203 loses the original status code information. You’ll mainly see it in corporate proxies with content filtering, security gateways, or academic research networks.
Example: A corporate proxy intercepts your API request to an external service. The origin returns 200 OK with user data including an email field. The proxy strips out the email for privacy and returns 203 Non-Authoritative Information instead of 200, signaling that it modified the response. You get the data, but you know it’s been altered.
Code Example
// Proxy server modifying responseconst http = require('http');const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});
const server = http.createServer((req, res) => { proxy.web(req, res, { target: 'http://origin-server.com' });});
// Intercept and modify responseproxy.on('proxyRes', (proxyRes, req, res) => { let body = [];
proxyRes.on('data', (chunk) => { body.push(chunk); });
proxyRes.on('end', () => { body = Buffer.concat(body).toString();
// Apply transformation (e.g., privacy filter) if (body.includes('email')) { const modified = JSON.parse(body); delete modified.email; // Remove sensitive field
// Send 203 instead of original 200 res.writeHead(203, { 'Content-Type': 'application/json', 'Warning': '214 Transformation Applied "Email field removed"' }); res.end(JSON.stringify(modified)); } });});Crash Course
203 Non-Authoritative Information, defined in RFC 9110 Section 15.3.4, indicates that while the request succeeded, a transforming proxy modified the response from the origin server’s original 200 OK response. The proxy acts as an intermediary that not only forwards but transforms content - filtering, transcoding, or modifying headers/body. By returning 203, the proxy signals to the client: ‘This response is not exactly what the origin sent; I modified it.’ This is semantically important for caching and validation - future cache validation requests might only be applicable along the same proxy path. Common transformations include: malware filtering, adult content blocking, format transcoding (WebP to JPEG for older browsers), privacy filtering (stripping tracking pixels), response compression, adding cache/mirror metadata, header normalization. The major limitation: changing status code to 203 loses information about the original status - it could have been 200, 201, 204, etc. This is why the deprecated Warning header (214 Transformation Applied) was often preferred - it preserves the original status while indicating transformation. 203 is cacheable by default. In practice, 203 is extremely rare. Most proxies either transparently pass through responses (preserving original status codes) or use Warning headers. You might encounter 203 in: corporate content filters, security gateways with threat scanning, CDNs doing aggressive optimization, academic/research proxies logging traffic, custom API gateways normalizing responses.
Example: A university network uses a transparent proxy for all outbound HTTP traffic. A student requests an academic paper from an external API (returns 200 OK with PDF in response body). The proxy scans the PDF for malware, finds it clean, but adds a watermark ‘Retrieved via University Network’ to track document distribution. The proxy returns 203 Non-Authoritative Information with the watermarked PDF and a Warning header explaining the transformation. The client receives the paper but knows it was modified.
Code Example
// Advanced proxy with content transformationconst express = require('express');const { createProxyMiddleware } = require('http-proxy-middleware');const sharp = require('sharp'); // Image processing
const app = express();
app.use('/api', createProxyMiddleware({ target: 'https://origin-api.com', changeOrigin: true,
onProxyRes: async (proxyRes, req, res) => { const contentType = proxyRes.headers['content-type'];
// Transform images: convert to WebP for modern browsers if (contentType && contentType.startsWith('image/')) { const acceptsWebP = req.headers['accept']?.includes('image/webp');
if (!acceptsWebP && proxyRes.statusCode === 200) { // Collect image data let chunks = []; proxyRes.on('data', chunk => chunks.push(chunk));
proxyRes.on('end', async () => { const imageBuffer = Buffer.concat(chunks);
// Convert to JPEG (transcoding transformation) const converted = await sharp(imageBuffer) .jpeg({ quality: 80 }) .toBuffer();
// Return 203 instead of 200 to signal transformation res.writeHead(203, { 'Content-Type': 'image/jpeg', 'Content-Length': converted.length, 'Warning': '214 Transformation Applied "Converted WebP to JPEG"', 'X-Proxy-Transformation': 'image-transcoding' });
res.end(converted); });
return; // Prevent default proxy behavior } }
// Transform JSON: apply privacy filter if (contentType && contentType.includes('application/json')) { let body = []; proxyRes.on('data', chunk => body.push(chunk));
proxyRes.on('end', () => { try { const jsonBody = JSON.parse(Buffer.concat(body).toString());
// Apply transformations const transformed = applyPrivacyFilter(jsonBody); const transformedStr = JSON.stringify(transformed);
// If we modified anything, return 203 if (transformedStr !== Buffer.concat(body).toString()) { res.writeHead(203, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(transformedStr), 'Warning': '214 Transformation Applied "Privacy filter applied"' }); res.end(transformedStr); } else { // No modifi...Deep Dive
The 203 Non-Authoritative Information status code, specified in RFC 9110 Section 15.3.4, indicates that the request was successful but a transforming proxy has modified the enclosed representation or metadata from the origin server’s 200 OK response. The term ‘non-authoritative’ means the response content or headers did not come directly from the origin server - they were transformed by an intermediary. Per the RFC, the purpose is to allow transforming proxies to notify recipients when transformations have been applied, as this knowledge might impact later decisions regarding the content. For instance, future cache validation requests for the content might only be applicable along the same request path (through the same proxies), since different proxies might apply different transformations.
Technical Details
The semantic distinction between 200 OK and 203 is subtle but important. Both indicate success, but 203 explicitly signals modification by an intermediary. RFC 9110 states that ‘a proxy that receives a 200 OK response for a request can forward it as a 203 Non-Authoritative Information response if the transformation applied is not the identity transformation.’ This impacts caching behavior and validation semantics.\n\nCaching implications: 203 responses are cacheable by default, following the same rules as 200. However, the Vary header becomes critical. If a proxy applies transformations based on request headers (e.g., Accept-Encoding, User-Agent), it must include appropriate Vary headers. Cache validation using ETags or Last-Modified is complicated because the validators might represent the transformed content, not the origin content. A cache revalidation request might receive a 304 Not Modified from the proxy (transformed content unchanged) while the origin content has actually changed.\n\nTransformation types vary widely. Content transformations include malware filtering, adult content blocking, compression/decompression, format transcoding (image/video format conversion), minification of JS/CSS, and injection of analytics scripts or headers. Header transformations include adding Via headers (required for proxies per RFC 9110), modifying Cache-Control directives, adding CORS headers, and stripping Set-Cookie for privacy. Protocol transformations include HTTP/2 to HTTP/1.1 downgrade, TLS termination, and header normalization.\n\nThe Warning header (deprecated but still used) provided an alternative mechanism. Warning: 214 Transformation Applied with descriptive text could be added to responses while preserving the original status code. This approach is generally superior because it doesn’t lose information about the original status (200 vs 201 vs 204). RFC 7234 deprecated Warning headers in favor of application-level mechanisms, but no direct replacement for signaling transformations emerged.\n\nSecurity and privacy considerations are significant. Transforming proxies can inject malicious content, strip security headers, or leak information. HTTPS (TLS/SSL) prevents intermediate transformation by encrypting end-to-end - proxies can’t inspect/modify HTTPS traffic without TLS interception (man-in-the-middle with trusted certificates). Some corporate networks deploy TLS-intercepting proxies with enterprise-trusted certificates, enabling transformation of HTTPS traffic. This is controversial due to privacy implications and introduces security risks if the proxy is compromised. The Sec-Fetch-* headers introduced in browsers help detect unexpected intermediaries.\n\nPractical alternatives to 203 include: preserving original status code and using custom headers (X-Proxy-Modified: true), implementing transformation as separate service (client explicitly requests transformation), using content negotiation (Accept headers) so transformation is expected, o…
Code Example
// Enterprise-grade transforming proxy with detailed signaling\nconst http = require('http');\nconst httpProxy = require('http-proxy');\nconst { createHash } = require('crypto');\nconst zlib = require('zlib');\n\nconst proxy = httpProxy.createProxyServer({\n target: 'http://origin-server.com',\n changeOrigin: true\n});\n\nconst transformationLog = [];\n\n// Track transformations for audit\nfunction logTransformation(type, details) {\n transformationLog.push({\n timestamp: new Date().toISOString(),\n type,\n details\n });\n}\n\nconst server = http.createServer((req, res) => {\n proxy.web(req, res, {}, (error) => {\n console.error('Proxy error:', error);\n res.writeHead(502);\n res.end('Bad Gateway');\n });\n});\n\nproxy.on('proxyRes', (proxyRes, req, res) => {\n const originalStatus = proxyRes.statusCode;\n const contentType = proxyRes.headers['content-type'] || '';\n \n // Only transform successful responses\n if (originalStatus !== 200) {\n return; // Pass through non-200 responses unchanged\n }\n \n let chunks = [];\n let transformationApplied = false;\n const transformations = [];\n \n proxyRes.on('data', (chunk) => {\n chunks.push(chunk);\n });\n \n proxyRes.on('end', () => {\n let body = Buffer.concat(chunks);\n let headers = { ...proxyRes.headers };\n \n // Transformation 1: Malware scanning\n if (contentType.includes('application/') || contentType.includes('text/')) {\n const isSafe = scanForMalware(body);\n if (!isSafe) {\n transformationApplied = true;\n transformations.push('Malware detected and removed');\n body = Buffer.from(JSON.stringify({\n error: 'Content blocked by security policy',\n reason: 'Malware detected'\n }));\n headers['content-type'] = 'application/json';\n logTransformation('malware-block', { url: req.url });\n }\n }\n \n // Transformation 2: Privacy filtering for JSON\n if (contentType.includes('application/json')) {\n try {\n const json = JSON.parse(body.toString());\n const filtered = applyPrivacyFilter(json);\n const filteredStr = JSON.stringify(filtered);\n \n if (filteredStr !== body.toString()) {\n transformationApplied = true;\n transformations.push('Privacy filter applied');\n body = Buffer.from(filteredStr);\n logTransformation('privacy-filter', { url: req.url });\n }\n } catch (e) {\n // Not valid JSON, skip\n }\n }\n \n // Transformation 3: Compression if not already compressed\n if (!headers['content-encoding'] && body.length > 1024) {\n const acceptEncoding = req.headers['accept-encoding'] || '';\n \n if (acceptEncoding.includes('gzip')) {\n body = zlib.gzipSync(body);\n headers['content-encoding'] = 'gzip';\n transformationApplied = true;\n transformations.push('Gzip compression applied');\n logTransf...Frequently Asked Questions
Why would a proxy return 203 instead of 200?
A proxy returns 203 when it modifies the response from the origin server - such as filtering content, transcoding formats, or changing headers. The 203 signals to the client 'I changed something - this isn't exactly what the origin sent.' This is important for caching and validation, as the transformed content might differ from what other proxies would return.
How is 203 different from using the Warning header?
The Warning header (214 Transformation Applied) can indicate transformations while preserving the original status code (200, 201, etc.). This is often better than 203 because you don't lose information about what the origin status was. 203 tells you something was modified, but you can't tell if the original was 200, 201, 204, or another 2xx code. The Warning header is now deprecated, but was generally superior.
Does HTTPS prevent 203 responses from proxies?
Yes, end-to-end HTTPS encryption prevents intermediate proxies from inspecting or modifying traffic, eliminating 203 scenarios. However, some corporate networks use TLS-intercepting proxies with enterprise-trusted certificates, which can decrypt, transform, and re-encrypt HTTPS traffic. This is controversial due to privacy and security implications.
Is 203 cacheable?
Yes, 203 responses are cacheable by default, just like 200 responses. However, caching is complicated because the ETag or Last-Modified validators might represent the transformed content, not the origin content. Cache revalidation might only be valid along the same proxy path, as different proxies could apply different transformations.
Why is 203 so rare in practice?
Most scenarios either don't need to signal transformations (just transparently modify), use custom headers instead (like X-Proxy-Modified), or can't transform due to HTTPS encryption. Additionally, losing the original status code information makes 203 less useful than alternatives like Warning headers or custom headers that preserve the original status.
Common Causes
- Corporate content filtering proxy removing inappropriate content
- Security gateway scanning for and blocking malware
- Privacy-focused proxy redacting sensitive fields (emails, IPs)
- CDN transcoding images to different formats for browser compatibility
- Academic/research proxy adding watermarks or tracking metadata
- API gateway normalizing response formats across multiple backends
- Compression proxy adding gzip encoding to uncompressed responses
- Caching proxy modifying cache-related headers
Implementation Guidance
- Client-side: Check for Warning header (214 Transformation Applied) to understand what changed
- Client-side: Be aware that validators (ETag, Last-Modified) might represent transformed content
- Client-side: Consider caching implications - transformed content might differ across proxy paths
- Proxy-side: Include Warning header with descriptive transformation details
- Proxy-side: Add Via header to identify the transforming proxy (required per RFC 9110)
- Proxy-side: Only use 203 for meaningful transformations, not trivial header changes
- Proxy-side: Consider using custom headers (X-Proxy-Modified) while preserving original status
- Proxy-side: Log transformations for audit and debugging purposes
- Avoid intermediate transformation: Use end-to-end HTTPS to prevent proxy modification
- Alternative: Implement transformation as explicit service rather than transparent proxy