Uploading files
Any file that is not HTML or XML — images, CSS, JavaScript, JSON, PDFs, fonts, archives — is stored as an opaque blob and served back byte-for-byte. Blobs are not parsed, composed, or addressable by selector.
Uploading
PUT the file bytes to a path, with the file's media type as the Content-Type:
If /data.json has
PUT /data.json HTTP/2
Host: 127.0.0.1
Content-Type: application/json
{"name":"widget","price":29.99}
Fetching the path returns the stored bytes unchanged, with the stored content type:
GET /data.json HTTP/2
Host: 127.0.0.1
HTTP/2 200
content-type: application/json
{"name":"widget","price":29.99}
POST to a directory creates a new blob under a generated name, the same way it creates HTML resources — see Resource Creation.
Content type
The stored content type decides how a document is served and whether it is treated as HTML, XML, or an opaque blob. It is resolved in this order:
| # | Rule |
|---|---|
| 1 | A .html / .htm path is always text/html, even if the request sends a different Content-Type. |
| 2 | Otherwise, the explicit Content-Type request header, if present. |
| 3 | Otherwise, inferred from the file extension — .css → text/css, .png → image/png, and so on. |
| 4 | Otherwise, application/octet-stream. |
Because the explicit header wins over the extension (rule 2 before rule 3), send the correct Content-Type for the file — or omit the header entirely and let the extension decide. A wrong or generic header (for example, a client that defaults every body to application/x-www-form-urlencoded) is stored verbatim and served back as-is.
Serving
A blob GET returns the stored bytes with the stored Content-Type and a content-hash strong ETag, so conditional requests revalidate cheaply. Static-asset types — CSS, JavaScript, images, and fonts — are additionally served with Cache-Control: public, max-age, so caches and CDNs between tiers can store them.
Blobs are opaque:
- No composition. Namespaced directives, expression bindings, and templates are not evaluated inside a blob.
- No selector addressing. A selector-range read or write is not applicable to a non-HTML/XML document and fails with
422 Unprocessable Content; selector operations require an HTML or XML document.
Storage
Blob bytes are held in an external object store, not the primary document database. An operator configures one or more backends — filesystem, s3, or azure; when several are configured, reads try each in list order and writes and deletes fan out to all of them. This is an operator concern: authoring is the same PUT / GET regardless of the backend.
See also
- PUT method — the request that stores a document or blob
- GET method — retrieving whole documents and fragments
- XML documents — the other non-HTML content family, which is parsed and composed
- Resource Creation — creating resources with
POSTto a directory