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

# Backend integration

Unpoly works with any backend that renders HTML. Requests from Unpoly look like regular browser requests, and the server answers with full pages, using the routes and templates it already has. Optional HTTP headers let the server shorten its responses, skip unchanged content, or steer the frontend.

<nav aria-label="Contents">

#### Contents

- [Plain HTTP by default](#plain-http-by-default)
- [Rendering only what is targeted](#rendering-only-what-is-targeted)
- [Skipping unchanged content](#skipping-unchanged-content)
- [Reacting to new deployments](#reacting-to-new-deployments)
- [Steering the frontend from the server](#steering-the-frontend-from-the-server)

</nav>

## Plain HTTP by default {#plain-http-by-default}

When the user follows a link that targets a fragment, Unpoly makes a request that any web framework can handle without a plugin:

```http
GET /users/5 HTTP/1.1
X-Up-Version: 3.15.0
X-Up-Target: .profile
```

The server renders the page for `/users/5` the same way it would for a full page load. Unpoly extracts the `.profile` element from the response and discards the rest, except [hungry elements](https://unpoly.com/learn/hungry-elements.md) and the `<head>`.

There is no JSON API to build and no separate fragment endpoint to maintain. Forms work the same way, with one convention: respond to an invalid submission with an error status like `422`, so Unpoly knows to re-render the form instead of the success target. See [Submitting forms: Handling validation errors](https://unpoly.com/learn/submitting-forms.md#validation). If you use [`[up-validate]`](https://unpoly.com/api/up-validate.md), also check for an [`X-Up-Validate`](https://unpoly.com/api/X-Up-Validate.md) header: validate the input and render the form again, but don't save it.

The `X-Up-*` headers above are part of an optional protocol. The sections below show how your server can use it to do less work.

## Rendering only what is targeted {#rendering-only-what-is-targeted}

The [`X-Up-Target`](https://unpoly.com/api/X-Up-Target.md) request header names the fragment that Unpoly will render. A server can skip the rest of the page and respond with only that element. A [`Vary`](https://unpoly.com/api/Vary.md) header then tells Unpoly that the response is specific to the target:

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

<div class="profile">...</div>
```

Other headers tell the server which layer is updating, so an overlay can get a page without site navigation, and whether the request comes from Unpoly at all.

<nav aria-label="Read more">

- [More: Optimizing responses](https://unpoly.com/learn/optimizing-responses.md)

</nav>

## Skipping unchanged content {#skipping-unchanged-content}

Unpoly reloads fragments when [polling](https://unpoly.com/learn/polling.md), [revalidating the cache](https://unpoly.com/learn/caching.md#revalidation) or calling [`up.reload()`](https://unpoly.com/api/up.reload.md). When the first response carried an [`ETag`](https://unpoly.com/api/ETag.md), Unpoly sends it back with the reload:

```http
GET /messages HTTP/1.1
If-None-Match: "x234dff"
```

When nothing changed, the server can answer with an empty `304 Not Modified` and Unpoly finishes the render pass without changes. The exchange then costs about 1 KB and no rendering time.

<nav aria-label="Read more">

- [More: Conditional requests](https://unpoly.com/learn/conditional-requests.md)

</nav>

## Reacting to new deployments {#reacting-to-new-deployments}

Since Unpoly updates fragments instead of loading new pages, users keep running the JavaScript and CSS they loaded when they opened the page. When a response contains scripts or stylesheets that differ from the current page, Unpoly emits an [`up:assets:changed`](https://unpoly.com/api/up:assets:changed.md) event:

```js
up.on('up:assets:changed', function() {
  // Show a banner offering to reload
})
```

You decide how to react: notify the user, reload at the next navigation, or swap in the new stylesheets.

<nav aria-label="Read more">

- [More: Reacting to new deployments](https://unpoly.com/learn/handling-asset-changes.md)

</nav>

## Steering the frontend from the server {#steering-the-frontend-from-the-server}

Response headers let the server change what Unpoly does with a response. It can change the target or render nothing, emit events on the client, update the title or the layer context, expire cache entries, or open or close an overlay. This response emits an event and closes an overlay with a result value, instead of rendering anything:

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

The full set of headers is small, and you only implement what you use. Libraries for many languages and frameworks wrap it in helpers. See [Protocol implementations](https://unpoly.com/learn/protocol-implementations.md).

<nav aria-label="Read more">

- [More: Server protocol](https://unpoly.com/learn/server-protocol.md)

</nav>

<nav aria-label="In this chapter">

## In this chapter {#children-index-title}

- [Optimizing responses](https://unpoly.com/learn/optimizing-responses.md)
- [Conditional requests](https://unpoly.com/learn/conditional-requests.md)
- [Reacting to new deployments](https://unpoly.com/learn/handling-asset-changes.md)
- [Server protocol](https://unpoly.com/learn/server-protocol.md)

</nav>

<nav aria-label="Reading order">

- [Previous: Script security](https://unpoly.com/learn/script-security.md)
- [Next: Optimizing responses](https://unpoly.com/learn/optimizing-responses.md)

</nav>

