HTMX Response Headers
Just as HTMX sends special headers with every request, your server can send special headers back in the response. These response headers let the server control HTMX behavior from the server side — triggering client-side events, redirecting to a new URL, pushing a new URL into the browser history, and more. This two-way header system is one of the most powerful features in HTMX.
The Two-Way Header System
CLIENT ──── HTMX Request Headers ────► SERVER
(who triggered, target, URL)
CLIENT ◄─── HTMX Response Headers ──── SERVER
(redirect, push URL, trigger event)
Complete Response Header Reference
| Header | What It Does |
|---|---|
| HX-Trigger | Fires a client-side event after the response is processed |
| HX-Trigger-After-Settle | Fires an event after the CSS settle period completes |
| HX-Trigger-After-Swap | Fires an event after the DOM swap completes |
| HX-Location | Performs a client-side HTMX redirect without a full page reload |
| HX-Redirect | Forces a full browser redirect to a new URL |
| HX-Push-Url | Pushes a new URL into the browser's history stack |
| HX-Replace-Url | Replaces the current URL in the browser's history (no new history entry) |
| HX-Reswap | Overrides the hx-swap value specified on the element |
| HX-Retarget | Overrides the hx-target value specified on the element |
| HX-Refresh | Triggers a full page refresh when set to "true" |
HX-Trigger: Fire Client Events From the Server
The HX-Trigger response header tells HTMX to fire a named event on the client after the response is processed. This lets the server coordinate multiple UI updates beyond the main swap.
Flask Example
from flask import Response, render_template
@app.route('/add-to-cart', methods=['POST'])
def add_to_cart():
# ... add item to cart ...
response = Response(
render_template('partials/cart-item.html'),
status=200
)
response.headers['HX-Trigger'] = 'cart-updated'
return response
On the client, any element listening for cart-updated responds automatically:
<!-- Cart counter refreshes whenever "cart-updated" fires -->
<div id="cart-count"
hx-get="/cart/count"
hx-trigger="cart-updated from:body"
hx-target="this">
0 items
</div>
Flow:
User clicks "Add to Cart"
|
v
POST /add-to-cart → Server adds item
|
v
Server returns cart item HTML + HX-Trigger: cart-updated
|
v
HTMX fires "cart-updated" event on the body
|
v
#cart-count's hx-trigger catches it
|
v
GET /cart/count → updates "3 items"
All in one user action — no extra JavaScript
Triggering Multiple Events
Send a JSON object in the header value to fire multiple events, optionally with data:
response.headers['HX-Trigger'] = json.dumps({
"cart-updated": {"item_id": 42, "quantity": 2},
"show-toast": {"message": "Item added to cart!"}
})
Client-side listeners receive the data in the event's detail object.
HX-Push-Url: Update the Browser URL Without a Reload
When HTMX updates a panel, the browser URL does not change by default. Use HX-Push-Url to push the relevant URL into the history, so the back button and sharing work correctly:
@app.route('/products/<int:product_id>')
def product_detail(product_id):
product = Product.query.get(product_id)
response = Response(render_template('partials/product-detail.html', product=product))
response.headers['HX-Push-Url'] = f'/products/{product_id}'
return response
Before click: browser URL = /products User clicks on Product #42 HTMX loads product detail into the panel After click: browser URL = /products/42 ← pushed by HX-Push-Url User presses Back: browser URL = /products ← history entry restored
HX-Redirect: Force a Full Page Navigation
After a successful login, you often need to redirect the user to the dashboard. Because HTMX only swaps fragments, a regular server-side redirect just loads the dashboard into the current target div. Use HX-Redirect to force a proper full-page navigation:
@app.route('/login', methods=['POST'])
def login():
user = authenticate(request.form)
if user:
session['user_id'] = user.id
response = Response('', status=200)
response.headers['HX-Redirect'] = '/dashboard'
return response
return render_template('partials/login-error.html'), 401
HX-Reswap and HX-Retarget: Override Client Attributes
These headers let the server change where and how the response is inserted — overriding whatever hx-swap and hx-target the element specified. This is useful when the server's response type determines the swap strategy:
# On error: retarget to the error panel and change swap strategy
@app.route('/process', methods=['POST'])
def process():
try:
result = do_processing()
return render_template('partials/success.html', result=result)
except Exception as e:
response = Response(
render_template('partials/error.html', message=str(e)),
status=422
)
response.headers['HX-Retarget'] = '#error-panel'
response.headers['HX-Reswap'] = 'innerHTML'
return response
HX-Refresh: Full Page Reload
response.headers['HX-Refresh'] = 'true'
HTMX receives this header and triggers a full browser page reload. Use it as a last resort when the page state has changed so fundamentally that a partial update is not sufficient.
Key Takeaway
HTMX response headers let your server drive client behavior without JavaScript. Use HX-Trigger to fire events that update other parts of the page, HX-Push-Url to maintain a correct browser URL, HX-Redirect to navigate to a new page, and HX-Reswap or HX-Retarget to change how and where the response lands. These headers make the server the single source of truth for application behavior — which is exactly the philosophy HTMX is built on.
