Skip to content

208 Already Reported

What is HTTP 208 Already Reported?

208 Already Reported
Imagine you have a folder with the same photo in three different places because ...
HTTP 208 Already Reported status code illustration

Explain Like I’m 3

You asked about a toy, and someone already told you about it! So instead of telling you the same thing again, they just say ‘I already said that!’ to save time!

Example: You ask ‘What color is my ball?’ and they say ‘Red!’ Then you ask again and they say ‘I already told you - red!’ They don’t repeat the whole description.

Explain Like I’m 5

Imagine you have a folder with the same photo in three different places because of shortcuts. When you ask the computer to list everything in the folder, it shows you the photo the first time and says all about it. But for the second and third copies, instead of describing the same photo again, it just says ‘208 Already Reported - this is the same photo I already told you about!’ This saves time and space, and it also prevents the computer from getting stuck in an endless loop if the shortcuts point to each other in a circle!

Example: You have a photo at /Photos/vacation.jpg and also shortcuts to it at /Favorites/vacation.jpg and /Best/vacation.jpg. When listing all files, the computer fully describes it the first time (200 OK), then says ‘208 Already Reported’ for the other two shortcuts instead of repeating everything.

Jr. Developer

HTTP 208 Already Reported is a WebDAV status code (RFC 5842) used inside 207 Multi-Status responses to avoid listing the same resource multiple times when bindings (similar to hard links or shortcuts) create multiple paths to the same resource. When a PROPFIND request with Depth: infinity traverses a collection, and the same resource appears multiple times due to bindings, the server returns full details (200 OK) for the first occurrence and 208 Already Reported for subsequent occurrences. This prevents: duplicate information in responses, infinite loops when bindings create cycles, wasted bandwidth from repeating data. You’ll only encounter 208 in WebDAV environments with binding support - standard file servers and REST APIs don’t use it. The 208 status appears within the multistatus XML response body, not as the HTTP response status itself (that’s 207). For backward compatibility, RFC 5842 specifies that 208 should only be used if the client signals binding support via the DAV request header

Example: A WebDAV client sends PROPFIND with Depth: infinity on /Projects/. Inside /Projects/, there are bindings: /Projects/docs/spec.pdf (original), /Projects/archive/spec.pdf (binding to same resource), /Projects/latest/spec.pdf (another binding). The server returns 207 Multi-Status with: spec.pdf at /Projects/docs/ (200 OK with full properties), spec.pdf at /Projects/archive/ (208 Already Reported), spec.pdf at /Projects/latest/ (208 Already Reported).

Code Example

// Conceptual WebDAV response with 208
// HTTP response status is 207, not 208
HTTP/1.1 207 Multi-Status
Content-Type: application/xml
<?xml version="1.0"?>
<D:multistatus xmlns:D="DAV:">
<!-- First occurrence: full details -->
<D:response>
<D:href>/docs/spec.pdf</D:href>
<D:propstat>
<D:prop>
<D:getcontentlength>1048576</D:getcontentlength>
<D:getlastmodified>Mon, 01 Jan 2026 12:00:00 GMT</D:getlastmodified>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
<!-- Binding to same resource: 208 -->
<D:response>
<D:href>/archive/spec.pdf</D:href>
<D:status>HTTP/1.1 208 Already Reported</D:status>
</D:response>
<!-- Another binding: 208 -->
<D:response>
<D:href>/latest/spec.pdf</D:href>
<D:status>HTTP/1.1 208 Already Reported</D:status>
</D:response>
</D:multistatus>

Crash Course

208 Already Reported, defined in RFC 5842 (Binding Extensions to WebDAV), is used exclusively within WebDAV 207 Multi-Status responses to indicate that a resource has already been enumerated earlier in the response and won’t be listed again. WebDAV bindings allow multiple URIs to reference the same resource (similar to hard links in filesystems). When a client performs PROPFIND with Depth: infinity on a collection containing bindings, the same resource might appear at multiple paths. Without 208, the server would list the resource multiple times, wasting bandwidth and potentially creating infinite loops if bindings form cycles (A binds to B, B binds to A). With 208, the server provides full details (200 OK) for the first occurrence and 208 for subsequent occurrences. The 208 status only appears for Depth: infinity requests - Depth: 0 and Depth: 1 don’t traverse deep enough to encounter duplicates. RFC 5842 specifies backward compatibility: servers SHOULD NOT send 208 unless the client signals binding support via DAV: <http://apache.org/dav/propset/fs/1> request header or similar. Clients not supporting bindings would see 208 as an unknown 2xx status and might ignore those resources. 208 is extremely rare - you’ll only see it in specialized WebDAV servers with binding support (Apache mod_dav with bindings, some document management systems). Standard file sync clients (Nextcloud, ownCloud) typically don’t use bindings. Modern REST APIs don’t use 208 - it’s WebDAV-specific.

Example: A WebDAV server manages a document library with bindings for project organization. The file report.pdf exists physically at /storage/reports/2026/report.pdf. Bindings exist at /projects/alpha/report.pdf and /projects/beta/report.pdf (both projects reference the same report). A client sends PROPFIND Depth: infinity on /projects/. The server traverses the tree and encounters report.pdf three times. Response: 207 Multi-Status with /storage/reports/2026/report.pdf (200 OK, full properties), /projects/alpha/report.pdf (208 Already Reported), /projects/beta/report.pdf (208 Already Reported). The client knows the file is shared across projects without receiving duplicate data.

Code Example

// WebDAV server with binding support
// Simplified conceptual implementation
class WebDAVServer {
async handlePROPFIND(req, res) {
const depth = req.headers.depth;
const path = req.url;
const supportsBindings = req.headers.dav?.includes('binding');
if (depth !== 'infinity') {
// No bindings deduplication needed for shallow requests
return this.standardPROPFIND(path, depth);
}
// Track resources by inode/ID to detect duplicates
const reportedResources = new Map(); // inode -> href
const responses = [];
// Traverse collection
const resources = await this.traverseCollection(path, depth);
for (const resource of resources) {
const inode = resource.stat.ino; // Filesystem inode
if (reportedResources.has(inode)) {
// This resource already reported
if (supportsBindings) {
responses.push({
href: resource.href,
status: 'HTTP/1.1 208 Already Reported'
});
} else {
// Client doesn't support bindings - include full details
responses.push(await this.buildFullResponse(resource));
}
} else {
// First occurrence - full details
reportedResources.set(inode, resource.href);
responses.push(await this.buildFullResponse(resource));
}
}
return this.build207Response(responses);
}
async buildFullResponse(resource) {
return {
href: resource.href,
propstats: [{
prop: {
getcontentlength: resource.stat.size,
getlastmodified: resource.stat.mtime,
resourcetype: resource.stat.isDirectory() ? '<D:collection/>' : ''
},
status: 'HTTP/1.1 200 OK'
}]
};
}
build207Response(responses) {
// Build multistatus XML...
}
}

Deep Dive

The 208 Already Reported status code, specified in RFC 5842 Section 7.1 (Binding Extensions to Web Distributed Authoring and Versioning), was introduced to address a specific problem in WebDAV: resource bindings creating multiple paths to the same underlying resource, leading to duplication in PROPFIND responses and potential infinite loops in cyclic binding structures. Per RFC 5842: ‘The 208 (Already Reported) status code can be used inside a DAV:propstat response element to avoid enumerating the internal members of multiple bindings to the same collection repeatedly. For each binding to a collection inside the request’s scope, only one will be reported with a 200 status, while subsequent DAV:response elements for all other bindings will use the 208 status, and no DAV:response elements for their descendants are included.’ This is a pure deduplication mechanism.

Technical Details

WebDAV bindings, defined in RFC 5842, are distinct from HTTP redirects or symbolic links. A binding is a server-side association between a segment in a collection and a resource. Multiple bindings can reference the same resource, creating multiple valid URIs for it. Unlike symlinks (which store paths and can break if targets move), bindings are first-class references. If the resource is modified via one binding, changes appear via all bindings. If a binding is deleted, the resource persists as long as one binding remains (reference counting). When all bindings are deleted, the resource is garbage collected.\n\nThe 208 status addresses two specific problems. First, bandwidth waste: Without 208, a PROPFIND Depth: infinity on a 10GB resource with 5 bindings would include 50GB of property data (10GB × 5). With 208, only 10GB is sent (full data once, minimal 208 response for others). Second, infinite loops: Binding A references collection B, and B references A, creating a cycle. Without 208, traversal loops infinitely. With 208, each collection is enumerated once, then marked Already Reported on subsequent encounters.\n\nImplementation requirements from RFC 5842: The 208 status should only be used when Depth: infinity is specified - shallower depths don’t traverse deep enough to encounter duplicates. Servers SHOULD NOT use 208 unless the client signals binding support, typically via DAV request header like DAV: 1, 2, binding or DAV: <http://apache.org/dav/propset/fs/1>. Clients not signaling support receive full responses for all bindings (backward compatibility). The 208 response MUST omit DAV:propstat elements - it’s purely a status indicator, not a property container.\n\nResource identification for deduplication varies by server. Filesystem-based WebDAV servers use inode numbers to detect hard links. Database-backed servers use resource IDs. The key is a stable identifier that remains constant across all bindings to the same resource. If resource identity can’t be determined (e.g., resources on different filesystems), conservative approach is to not use 208 and send full responses.\n\nClient handling: Clients supporting bindings maintain a map of reported resources (by href or resource-id property). When encountering 208, they skip processing that resource (it was already processed on first occurrence). Clients not supporting bindings typically ignore 208 (treating it as generic success) and might display duplicates.\n\nCyclic binding detection: Servers must track visited resources to prevent infinite loops. Common approach: maintain a Set of visited inodes/IDs during traversal. When a resource is encountered, check if it’s in the Set. If yes, return 208 and don’t descend into it. If no, add to Set, return 200, and descend if it’s a collection. This ensures termination even in complex cyclic structures.\n\nHTTP/2 and HTTP/3 don’t change 208 semantics. The response is still a 207 Multi-Status at the HTTP level, with 208 appearing in the XML body. We…

Code Example

// Production WebDAV server with cycle detection\nconst fs = require('fs').promises;\nconst path = require('path');\nconst xml2js = require('xml2js');\n\nclass BindingAwareWebDAV {\n async handlePROPFIND(req, res) {\n const requestPath = req.path;\n const depth = req.headers.depth || '0';\n const davHeader = req.headers.dav || '';\n const supportsBindings = davHeader.includes('binding');\n \n if (depth !== 'infinity') {\n return this.standardPROPFIND(requestPath, depth, res);\n }\n \n const basePath = path.join(__dirname, 'webdav', requestPath);\n const reportedInodes = new Map(); // inode -> first href\n const responses = [];\n \n // Traverse with cycle detection\n await this.traverseWithCycleDetection(\n basePath,\n requestPath,\n reportedInodes,\n responses,\n supportsBindings\n );\n \n const multistatusXML = this.buildMultistatusXML(responses);\n \n res.status(207)\n .set('Content-Type', 'application/xml; charset=utf-8')\n .send(multistatusXML);\n }\n \n async traverseWithCycleDetection(\n fsPath,\n davPath,\n reportedInodes,\n responses,\n supportsBindings,\n visitedInodes = new Set()\n ) {\n try {\n const stat = await fs.stat(fsPath);\n const inode = stat.ino;\n \n // Check if we've already reported this resource\n if (reportedInodes.has(inode)) {\n // Already reported - use 208 if client supports it\n if (supportsBindings) {\n responses.push({\n href: davPath,\n status: 'HTTP/1.1 208 Already Reported'\n });\n } else {\n // Client doesn't support bindings - send full response\n responses.push(await this.buildFullResponse(fsPath, davPath, stat));\n }\n return; // Don't descend - already processed\n }\n \n // Check for cycles (visited but not yet reported)\n if (visitedInodes.has(inode)) {\n // Cycle detected!\n console.warn('Cycle detected at', davPath);\n if (supportsBindings) {\n responses.push({\n href: davPath,\n status: 'HTTP/1.1 208 Already Reported'\n });\n }\n return;\n }\n \n // Mark as visited (for cycle detection)\n visitedInodes.add(inode);\n \n // First occurrence - full response\n reportedInodes.set(inode, davPath);\n responses.push(await this.buildFullResponse(fsPath, davPath, stat));\n \n // If it's a directory, descend\n if (stat.isDirectory()) {\n const entries = await fs.readdir(fsPath);\n \n for (const entry of entries) {\n await this.traverseWithCycleDetection(\n path.join(fsPath, entry),\n davPath + '/' + entry,\n reportedInodes,\n responses,\n supportsBindings,\n new Set(visitedInodes) // Clone to avoid sibling interference\n );\n ...

Frequently Asked Questions

When would I see 208 Already Reported?

You'll only see 208 in WebDAV environments with binding support when performing PROPFIND with Depth: infinity on collections containing bindings (multiple paths to the same resource). It appears inside 207 Multi-Status XML responses, not as the HTTP status itself. Standard file servers and REST APIs don't use 208 - it's extremely rare and WebDAV-specific.

What's the difference between 208 and 200 in a multistatus response?

200 OK in a multistatus response means 'here are the full properties for this resource.' 208 Already Reported means 'this resource was already listed earlier in this response with full details - I'm just acknowledging it exists here too without repeating the data.' 208 saves bandwidth and prevents infinite loops in cyclic binding structures.

Do I need to handle 208 in my WebDAV client?

Only if your client explicitly supports WebDAV bindings. If you don't signal binding support (via DAV header), servers shouldn't send 208. If you do receive 208, you can safely ignore it (treat as generic 2xx success) - it means that resource was already processed earlier in the response. Most WebDAV clients don't explicitly support bindings.

How does 208 prevent infinite loops?

When WebDAV bindings form cycles (collection A contains B, B contains A), traversing without deduplication loops forever. With 208, the server tracks which resources it's already reported. On first encounter, it returns 200 with full details. On subsequent encounters (including cycles), it returns 208 and doesn't descend further, guaranteeing termination.

Can 208 be used outside WebDAV?

Technically yes (it's a registered HTTP status code), but it has no meaning outside WebDAV binding contexts. REST APIs handling duplicate references could theoretically use 208, but it's non-standard and clients wouldn't understand it. Better to use application-level deduplication in JSON responses. 208 is essentially WebDAV-only.

Common Causes

  • WebDAV PROPFIND Depth: infinity on collection with resource bindings
  • WebDAV server with hard links (filesystem-level bindings) traversing directory trees
  • Document management system with bindings allowing documents in multiple folders
  • WebDAV collection with cyclic bindings (folder contains itself indirectly)
  • PROPFIND on shared resources where same file appears at multiple paths

Implementation Guidance

  • Server-side: Only send 208 if client signals binding support via DAV header
  • Server-side: Only use 208 for Depth: infinity requests (not 0 or 1)
  • Server-side: Track reported resources by inode/resource-ID to detect duplicates
  • Server-side: Maintain visited set to detect and break cycles
  • Server-side: Return 200 for first occurrence, 208 for subsequent occurrences
  • Server-side: Don’t include propstat elements in 208 responses - just status
  • Client-side: Send DAV header with binding support if you handle bindings
  • Client-side: Maintain map of reported resources when processing 207 responses
  • Client-side: Skip processing when encountering 208 (already processed earlier)
  • Client-side: If you don’t support bindings, treat 208 as generic 2xx (ignore)
  • Alternative: Avoid bindings entirely - use collections of references or separate copies
📚 Sources:
🕐 Last updated: January 5, 2026

Comments