---
area: "Learn"
parent: "Backend integration chapter"
url: "https://unpoly.com/learn/server-protocol"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [Backend integration chapter](https://unpoly.com/learn/backend-integration.md)</nav>

# Server protocol

Your backend can inspect and manipulate Unpoly's rendering through plain HTTP headers: request headers tell the server what Unpoly is about to render, and response headers let the server change what happens on the client. This exchange is Unpoly's server protocol, documented header by header in [`up.protocol`](https://unpoly.com/api/up.protocol.md). None of it is required, and you can implement only the parts you need.

Libraries that implement the protocol exist for many languages and frameworks. See [Protocol implementations](https://unpoly.com/learn/protocol-implementations.md).

<nav aria-label="Contents">

#### Contents

- [What the server can read](#request-headers)
- [What the server can send](#response-headers)
- [Other conventions](#conventions)

</nav>

## What the server can read {#request-headers}

Every request that Unpoly makes carries headers describing its purpose. For a link that updates a `.menu` element in a drawer overlay, the server sees:

```http
GET /sitemap HTTP/1.1
X-Up-Version: 3.15.0
X-Up-Target: .menu
X-Up-Mode: drawer
```

**The frontend's version** arrives in [`X-Up-Version`](https://unpoly.com/api/X-Up-Version.md). Its presence alone tells an Unpoly request from a full page load.

**The targeted fragment** arrives as a CSS selector in [`X-Up-Target`](https://unpoly.com/api/X-Up-Target.md). A server can [render only that fragment](https://unpoly.com/learn/optimizing-responses.md#target) and skip the rest of the page. When the server responds with an error status, Unpoly renders a different target, which is sent as [`X-Up-Fail-Target`](https://unpoly.com/api/X-Up-Fail-Target.md).

**The targeted layer** is described by [`X-Up-Mode`](https://unpoly.com/api/X-Up-Mode.md), the [mode](https://unpoly.com/api/up.layer.mode.md) of the layer being updated, and [`X-Up-Fail-Mode`](https://unpoly.com/api/X-Up-Fail-Mode.md) for error responses. [`X-Up-Origin-Mode`](https://unpoly.com/api/X-Up-Origin-Mode.md) is the mode of the layer containing the link or form that made the request, in case it targets another layer. A server may [render leaner pages for overlays](https://unpoly.com/learn/optimizing-responses.md#mode).

**The layer's context** arrives as JSON in [`X-Up-Context`](https://unpoly.com/api/X-Up-Context.md) (and [`X-Up-Fail-Context`](https://unpoly.com/api/X-Up-Fail-Context.md)), so a server can [vary a reused screen](https://unpoly.com/learn/layer-context.md#reuse-interaction-with-variation) by the data a layer was opened with.

**Validation requests** carry the names of the fields being validated in [`X-Up-Validate`](https://unpoly.com/api/X-Up-Validate.md). The server is expected to validate the submission without saving it and to render the form again, as described in [Validating forms](https://unpoly.com/learn/validation.md).

**The known version of a reloaded fragment** arrives as [`If-None-Match`](https://unpoly.com/api/If-None-Match.md) or [`If-Modified-Since`](https://unpoly.com/api/If-Modified-Since.md), so the server can answer with an empty `304 Not Modified` when nothing changed. See [Conditional requests](https://unpoly.com/learn/conditional-requests.md).

A response that depends on any of these headers should name them in a [`Vary`](https://unpoly.com/api/Vary.md) header, so that Unpoly [partitions its cache](https://unpoly.com/learn/optimizing-responses.md#vary) by their values.

## What the server can send {#response-headers}

Response headers let the server override what Unpoly would do with the response. All of them are optional. Without them, Unpoly renders the targeted fragment from the response body.

**Changing the target.** An [`X-Up-Target`](https://unpoly.com/api/X-Up-Target.md) response header replaces the selector that Unpoly will render. The value [`:none`](https://unpoly.com/api/none.md) finishes the render pass [without changes](https://unpoly.com/learn/conditional-requests.md#rendering-nothing):

```http
HTTP/1.1 200 OK
X-Up-Target: .flashes

<div class="flashes">Your changes were saved.</div>
```

**Setting the document title.** When a [shortened response](https://unpoly.com/learn/optimizing-responses.md#title) has no `<title>`, an [`X-Up-Title`](https://unpoly.com/api/X-Up-Title.md) header sets it. The value is a [relaxed JSON](https://unpoly.com/api/relaxed-json.md) string.

**Correcting the browser location.** After a redirect, Unpoly uses the final URL for the browser's address bar. It cannot see a change of the HTTP method, e.g. when `POST /users` redirects to `GET /users`. Send [`X-Up-Location`](https://unpoly.com/api/X-Up-Location.md) and [`X-Up-Method`](https://unpoly.com/api/X-Up-Method.md) to make the final URL and method explicit:

```http
HTTP/1.1 200 OK
X-Up-Location: /users
X-Up-Method: GET
```

**Emitting events on the client.** [`X-Up-Events`](https://unpoly.com/api/X-Up-Events.md) takes a relaxed JSON array of event objects. Each object's `type` becomes the event type, other properties become properties of the event:

```http
HTTP/1.1 200 OK
X-Up-Events: [{ type: 'user:created', id: 5012 }]
```

**Closing or opening overlays.** [`X-Up-Accept-Layer`](https://unpoly.com/api/X-Up-Accept-Layer.md) and [`X-Up-Dismiss-Layer`](https://unpoly.com/api/X-Up-Dismiss-Layer.md) [close the targeted overlay](https://unpoly.com/learn/closing-overlays.md#from-server) with the given result value, instead of rendering the response. [`X-Up-Open-Layer`](https://unpoly.com/api/X-Up-Open-Layer.md) renders the response in a new overlay. See [Subinteractions](https://unpoly.com/learn/subinteractions.md) for the pattern these headers enable.

**Updating the layer context.** An [`X-Up-Context`](https://unpoly.com/api/X-Up-Context.md) response header carries the changed keys. See [Changing context from the server](https://unpoly.com/learn/layer-context.md#server-updates).

**Controlling the cache.** When Unpoly sends a non-`GET` request, it expires its entire cache. [`X-Up-Expire-Cache`](https://unpoly.com/api/X-Up-Expire-Cache.md) and [`X-Up-Evict-Cache`](https://unpoly.com/api/X-Up-Evict-Cache.md) expire or evict additional cache entries matching a [URL pattern](https://unpoly.com/api/url-patterns.md):

```http
HTTP/1.1 200 OK
X-Up-Expire-Cache: /notes/*
```

**Versioning content.** An [`ETag`](https://unpoly.com/api/ETag.md) or [`Last-Modified`](https://unpoly.com/api/Last-Modified.md) header lets the server answer later reloads with `304 Not Modified`. See [Conditional requests](https://unpoly.com/learn/conditional-requests.md).

The [`up.protocol`](https://unpoly.com/api/up.protocol.md) reference lists every header with its exact format.

## Other conventions {#conventions}

A few parts of the protocol are not headers:

- Unpoly sends a [CSRF token](https://unpoly.com/api/up.protocol.config.md#config.csrfToken) with unsafe requests, read from a `<meta name="csrf-token">` tag. The header name defaults to `X-CSRF-Token` and is configured in [`up.protocol.config.csrfHeader`](https://unpoly.com/api/up.protocol.config.md#config.csrfHeader).
- Unpoly sends a request with a method other than `GET` or `POST` as a `POST`, with the original method in a `_method` [parameter](https://unpoly.com/api/up.protocol.config.md#config.methodParam). Fragment updates can opt out with [`up.network.config.wrapMethod`](https://unpoly.com/api/up.network.config.md#config.wrapMethod).
- A server can set a [`_up_method` cookie](https://unpoly.com/api/_up_method.md) to tell Unpoly the method of the initial page load. This lets Unpoly handle pages that were loaded with a `POST` request.

A protocol implementation for your framework wraps all of this in helpers. Most handle the [`_up_method`](https://unpoly.com/api/_up_method.md) cookie for you, and some, like `unpoly-rails`, also set the [`Vary`](https://unpoly.com/api/Vary.md) header when you read a request header. See [Protocol implementations](https://unpoly.com/learn/protocol-implementations.md) for the list.

<nav aria-label="Reading order">

- [Previous: Reacting to new deployments](https://unpoly.com/learn/handling-asset-changes.md)
- [Next: Advanced rendering](https://unpoly.com/learn/advanced-rendering.md)

</nav>

