# Hono: middleware, cookies, headers, redirects

> Hono middleware, cookies, headers, and redirects for everyday web apps. Built-in logger, basic auth, and CORS, plus custom middleware and signed cookies.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-08-09 | Updated: 2026-08-03 | Topics: [JavaScript](https://flaviocopes.com/tags/js/) | Canonical: https://flaviocopes.com/hono-middleware-cookies-headers/

In the last post we covered the basics of [Hono](https://flaviocopes.com/hono/) on [Bun](https://flaviocopes.com/bun/). We set up a small app, defined routes, and returned JSON and plain text. Now let's look at the tools you reach for on almost every real project: middleware, headers, cookies, and redirects.

## Middleware

Middleware is a function that runs during the request–response cycle. It can run before your route handler, after it, or both. You register it with `app.use()` and a path pattern.

The pattern works like a route path. `'*'` runs on every request. `'/api/*'` runs only on paths that start with `/api/`. Middleware on a matching path runs before the route handler for that same path.

Hono ships with built-in middleware. A logger is the easiest place to start:

```js
import { logger } from 'hono/logger'

app.use('*', logger())
```

This logs every request to the console. Handy while you're building.

Need to protect a route? Use HTTP basic auth:

```js
import { basicAuth } from 'hono/basic-auth'

app.use('/dashboard/*', basicAuth({
  username: 'admin',
  password: 'secret',
}))

app.get('/dashboard', (c) => c.text('Logged in'))
```

Wrong credentials get a 401. The route handler only runs when the client passes the check.

For APIs that talk to a browser on another origin, add [CORS](https://flaviocopes.com/cors/):

```js
import { cors } from 'hono/cors'

app.use('/api/*', cors())
```

That covers the common built-ins. Hono has more — compression, cache headers, JWT validation, and others. Check the [official middleware docs](https://hono.dev/middleware) when you need something specific.

You can also write your own middleware. The pattern is always the same — an async function that receives the context `c` and a `next` function:

```js
app.use('*', async (c, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  c.header('X-Response-Time', `${ms}ms`)
})
```

Call `await next()` to pass control to the next middleware or route handler. Code after `next()` runs on the way out, so you can measure response time or tweak headers before the response goes back to the client.

You can stack middleware. Logger on `'*'`, CORS on `'/api/*'`, basic auth on `'/admin/*'` — each one runs in the order you registered it.

## Headers

Every HTTP request and response carries [HTTP response headers](https://flaviocopes.com/http-response-headers/). Hono makes both sides easy.

Reading request headers is straightforward. Pass the header name to `c.req.header()`:

```js
app.get('/', (c) => {
  const userAgent = c.req.header('User-Agent')
  return c.text(userAgent ?? '')
})
```

Need every header on the request? Use `c.req.raw.headers` — it's the underlying `Headers` object from the Web Fetch API.

To set a response header, use `c.header()`:

```js
c.header('Content-Type', 'text/html')
```

You can call it inside a route handler or in middleware after `await next()`. Multiple calls add multiple headers.

## Cookies

Hono provides helpers for [cookies](https://flaviocopes.com/cookies/) in `hono/cookie`. Import what you need:

```js
import { getCookie, setCookie, deleteCookie } from 'hono/cookie'
```

The context `c` is always the first argument. Set a cookie like this:

```js
setCookie(c, 'username', 'Flavio')
```

Read it back:

```js
const username = getCookie(c, 'username')
```

Delete it:

```js
deleteCookie(c, 'username')
```

You can pass an options object as the fourth argument. The useful ones are `httpOnly`, `path`, `expires`, `sameSite`, and `secure`:

```js
setCookie(c, 'session', 'abc123', {
  path: '/',
  httpOnly: true,
  secure: true,
  sameSite: 'Strict',
  expires: new Date(Date.now() + 900000),
})
```

`httpOnly` keeps the cookie off client-side JavaScript. `secure` sends it only over HTTPS. `sameSite` controls cross-site behavior.

For cookies that hold sensitive data, use signed cookies:

```js
import { getSignedCookie, setSignedCookie } from 'hono/cookie'

await setSignedCookie(c, 'token', 'user-42', 'my-secret')
const token = await getSignedCookie(c, 'my-secret', 'token')
```

`setSignedCookie()` and `getSignedCookie()` work like the regular helpers but add a signature. If someone changes the value in the browser, the signature won't match and you can reject it.

## Redirects

Redirects are common in web apps. Hono gives you `c.redirect()`:

```js
return c.redirect('/there')
```

The default status is 302. For a permanent redirect, pass 301:

```js
return c.redirect('/there', 301)
```

Use 302 when the destination might change — login flows, temporary moves. Use 301 when the old URL is gone for good and search engines should update their index.

That's the everyday toolkit. Middleware for cross-cutting logic, headers for metadata, cookies for session state, redirects for navigation. In the next post in this series we'll deploy a Hono app to Cloudflare Workers.
