# How htmx Handles HTTP Responses Based on Status Codes

> Discover how htmx intelligently processes HTTP responses by status code. Learn about content swaps, error events, and redirects directly from the source code.

- Repository: [Big Sky Software/htmx](https://github.com/bigskysoftware/htmx)
- Tags: deep-dive
- Published: 2026-08-30

---

**htmx uses the HTTP status code of every XHR or fetch response to determine whether to execute a content swap, trigger an error event, or process a special redirect, with the core logic implemented in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js).**

The bigskysoftware/htmx library treats HTTP status codes as the primary control mechanism for DOM updates and error handling. When a request completes, htmx inspects `xhr.status` to route the response through either a success pipeline that executes content swaps or an error pipeline that triggers cleanup events. This approach allows developers to declaratively handle everything from successful 200 OK responses to custom 286 redirects using standard HTML attributes.

## HTTP Status Code Processing Ranges

The htmx engine categorizes incoming responses into distinct groups based on their numeric status codes, applying different swap and event strategies to each.

### Successful Responses (200–399)

Responses in the 200–399 range are considered successful. According to the source at `src/htmx.js#L3320-L3324`, htmx parses the response body and executes the swap strategy defined by `hx-swap` (defaulting to `"innerHTML"`). After swapping, the library fires the standard lifecycle events including `htmx:afterSwap` and `htmx:afterSettle`.

```html
<div id="target" hx-get="/fragments/widget" hx-swap="outerHTML"></div>

```

When `/fragments/widget` returns **200**, the HTML fragment replaces the content of `#target`, and the full event chain runs.

### Error Responses (400 and Above)

Status codes 400 and above trigger the error handling path. As implemented in `src/htmx.js#L4965-L4970`, htmx calls `triggerErrorEvent` and passes an `ErrorInfo` object containing the status, URL, and optional error message. By default, no swap is performed, though the `htmx:responseError` event bubbles up for application-level handling.

```html
<div id="msg" hx-get="/api/missing" hx-trigger="load"></div>

<script>
  document.body.addEventListener('htmx:responseError', function (evt) {
    const { error, status } = evt.detail;
    document.querySelector('#msg').textContent = `Error ${status}: ${error}`;
  });
</script>

```

If the server returns **404**, the swap is skipped and the custom listener displays the failure message.

### The Special Case of Status 286

Status code 286 serves as a built-in "response-target" shortcut for redirects. Located at `src/htmx.js#L4894-L4899`, this logic checks for a `Location` header or custom `HX-Redirect` header to replace the current page without a full reload. Importantly, `htmx:responseError` is **not** triggered for 286 responses.

```html
<div hx-get="/redirect-me" hx-response-286="redirect"></div>

```

When the server replies with **286** and a `Location` header, htmx follows the redirect automatically.

### Custom Status Code Matching

Developers can define fine-grained rules using `hx-response-*` attributes, which support regular expressions to match specific status codes. The configuration parsing resides around `src/htmx.js#L4742-L4759`, allowing arbitrary status codes to trigger specific swap behaviors or events independent of the default success/error dichotomy.

## Internal Request Handling Flow

The request lifecycle follows a strict inspection sequence after the XHR `load` event fires.

1. **Status Inspection** – The engine reads `xhr.status` and compares it against configured matchers and the standard ranges.
2. **Success Path** (200–399) – Invokes `handleResponse`, which executes `swapContent` and triggers the normal lifecycle events.
3. **Error Path** (≥400) – Invokes `triggerErrorEvent`, creating an `ErrorInfo` object that bubbles through the DOM so developers can listen for `htmx:responseError`.
4. **Special Handling** – Status 286 and custom regex matches from `hx-response-*` attributes can intercept the flow before standard success or error processing occurs.

## Practical Implementation Examples

### Swapping Content on Errors with hx-error-swap

Use the `hx-error-swap` attribute to force content insertion even when the server returns an error status code.

```html
<div hx-get="/api/failure" hx-error-swap="innerHTML">
  Loading…
</div>

```

Even for a **500** response, the response body will be swapped into the element because `hx-error-swap` overrides the default error skip logic.

### Handling Specific Status Codes

You can combine standard event listeners with status-specific logic to handle partial failures or validation errors differently.

```javascript
document.body.addEventListener('htmx:responseError', function (evt) {
  if (evt.detail.xhr.status === 422) {
    // Handle validation error specifically
    alert('Validation failed');
  } else if (evt.detail.xhr.status === 503) {
    // Handle service unavailable
    alert('Service temporarily unavailable');
  }
});

```

## Key Source Files and Extensions

| File | Role |
|------|------|
| [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js) | Core engine containing `handleResponse`, `triggerErrorEvent`, and status-checking logic at lines 3320–3324, 4894–4899, and 4965–4970. |
| [`dist/ext/response-targets.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/ext/response-targets.js) | Extension that adds support for the custom 286 status and `HX-Redirect` handling. |
| [`dist/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/dist/htmx.js) | Compiled, browser-ready version mirroring the core logic. |

## Summary

- **htmx categorizes status codes** into success (200–399), error (≥400), and special cases (286) to determine swap behavior.
- **Successful responses** trigger `handleResponse` and `swapContent`, firing `htmx:afterSwap` and `htmx:afterSettle`.
- **Error responses** invoke `triggerErrorEvent` with an `ErrorInfo` object containing status, URL, and message details.
- **Use `hx-error-swap`** to force content swapping on error status codes that would normally be skipped.
- **Status 286** bypasses error handling and processes redirects via `HX-Redirect` or `Location` headers without firing `htmx:responseError`.
- **Custom regex patterns** in `hx-response-*` attributes enable fine-grained status code handling beyond the default ranges.

## Frequently Asked Questions

### Does htmx swap content when it receives a 404 or 500 status code?

By default, no. Status codes 400 and above prevent content swapping unless you explicitly add the `hx-error-swap` attribute to the initiating element. Instead, htmx fires the `htmx:responseError` event, allowing your application logic to handle the failure gracefully.

### How can I redirect the browser when the server returns a specific status code?

Configure your server to return status **286** along with a `Location` or `HX-Redirect` header. According to the source at `src/htmx.js#L4894-L4899`, htmx interprets this combination as a redirect instruction and updates `window.location` without triggering error events or performing a swap.

### What is the difference between `htmx:afterSwap` and `htmx:responseError` events?

`htmx:afterSwap` fires immediately after htmx inserts content into the DOM during successful requests (status 200–399). Conversely, `htmx:responseError` fires for status codes 400 and above before any swap occurs (unless overridden by `hx-error-swap`), providing an `ErrorInfo` object with the failed request details.

### Can I handle specific status codes like 422 differently in htmx?

Yes. Use the `hx-response-*` attribute pattern with regular expressions to match specific status codes. As implemented around `src/htmx.js#L4742-L4759`, these attributes allow you to define custom swap targets or behaviors for individual status codes, overriding the default success or error handling for those specific cases.