The Proxy helper provides a secure and convenient way to proxy HTTP requests through your Hono application. It automatically handles hop-by-hop headers, encoding, and provides security features to prevent header injection attacks.
Import
import { proxy } from 'hono/proxy'
Basic Usage
Proxy requests to another server:
import { proxy } from 'hono/proxy'
const originServer = 'example.com'
app . get ( '/proxy/:path' , ( c ) => {
const path = c . req . param ( 'path' )
return proxy ( `http:// ${ originServer } / ${ path } ` )
})
Function Signature
function proxy (
input : string | URL | Request ,
init ?: ProxyRequestInit
) : Promise < Response >
input
string | URL | Request
required
The destination URL or Request object to proxy to
Optional configuration object (extends RequestInit with proxy-specific options)
Returns : Promise<Response> - A Response object ready to be returned from your handler
Proxy Options
raw
Pass the original request to copy method, body, and headers:
app . all ( '/proxy/:path' , ( c ) => {
return proxy ( `http:// ${ originServer } / ${ c . req . param ( 'path' ) } ` , {
raw: c . req . raw ,
})
})
Original Request object to copy method, body, and headers from
Customize or override headers:
app . get ( '/proxy/:path' , ( c ) => {
return proxy ( `http:// ${ originServer } / ${ c . req . param ( 'path' ) } ` , {
headers: {
... c . req . header (),
'X-Forwarded-For' : '127.0.0.1' ,
'X-Forwarded-Host' : c . req . header ( 'host' ),
Authorization: undefined , // Remove Authorization header
},
})
})
Headers to send with the proxied request. Set to undefined to remove a header.
customFetch
Use a custom fetch implementation:
const customFetch = async ( request : Request ) => {
console . log ( 'Proxying request:' , request . url )
return fetch ( request )
}
app . get ( '/proxy/:path' , ( c ) => {
return proxy ( `http:// ${ originServer } / ${ c . req . param ( 'path' ) } ` , {
customFetch ,
})
})
customFetch
(request: Request) => Promise<Response>
Custom fetch function for making the proxied request
strictConnectionProcessing
Enable RFC 9110 compliant Connection header processing:
app . get ( '/internal-proxy/:path' , ( c ) => {
return proxy ( `http:// ${ internalServer } / ${ c . req . param ( 'path' ) } ` , {
raw: c . req . raw ,
strictConnectionProcessing: true ,
})
})
strictConnectionProcessing
false (default): Ignores Connection header to prevent Hop-by-Hop Header Injection attacks. Recommended for untrusted clients.
true: Processes Connection header per RFC 9110 and removes listed headers. Only use in trusted environments.
Security : Only enable strictConnectionProcessing in trusted environments. When false (default), the Connection header is ignored to prevent potential security vulnerabilities.
Complete Example
Simple Proxy
import { Hono } from 'hono'
import { proxy } from 'hono/proxy'
const app = new Hono ()
app . get ( '/proxy' , ( c ) => {
return proxy ( 'https://example.com/' )
})
export default app
Forward All Request Data
Proxy the entire request including method, body, and headers:
import { proxy } from 'hono/proxy'
const originServer = 'api.example.com'
app . all ( '/api/*' , ( c ) => {
const path = c . req . path . replace ( / ^ \/ api/ , '' )
return proxy ( `https:// ${ originServer }${ path } ` , {
raw: c . req . raw ,
headers: {
'X-Forwarded-Host' : c . req . header ( 'host' ),
},
})
})
Modify Response
Modify the proxied response before returning:
import { proxy } from 'hono/proxy'
app . get ( '/proxy/:path' , async ( c ) => {
const res = await proxy ( `http:// ${ originServer } / ${ c . req . param ( 'path' ) } ` , {
headers: {
'X-Forwarded-For' : '127.0.0.1' ,
},
})
// Remove sensitive headers from response
res . headers . delete ( 'Set-Cookie' )
res . headers . delete ( 'X-Internal-Header' )
return res
})
Add Authentication
Add authentication to proxied requests:
import { proxy } from 'hono/proxy'
app . get ( '/secure-proxy/*' , async ( c ) => {
// Verify client authentication
const token = c . req . header ( 'Authorization' )
if ( ! token ) {
return c . json ({ error: 'Unauthorized' }, 401 )
}
const path = c . req . path . replace ( / ^ \/ secure-proxy/ , '' )
return proxy ( `https:// ${ originServer }${ path } ` , {
headers: {
'Authorization' : 'Bearer secret-backend-token' ,
'X-Client-Token' : token ,
},
})
})
Handle Query Parameters
import { proxy } from 'hono/proxy'
app . get ( '/search' , ( c ) => {
const query = c . req . query ( 'q' )
const url = new URL ( 'https://api.example.com/search' )
url . searchParams . set ( 'q' , query )
return proxy ( url . toString ())
})
Automatic Processing
The proxy helper automatically:
The following headers are automatically removed from both request and response per RFC 2616 :
Connection
Keep-Alive
Proxy-Authenticate
Proxy-Authorization
TE
Trailer
Transfer-Encoding
Upgrade
Handles Accept-Encoding
The Accept-Encoding header is automatically removed from the request. The runtime will handle compression natively.
Handles Content-Encoding
If the response has Content-Encoding, it’s removed along with Content-Length to prevent decompression issues.
Security Considerations
Hop-by-Hop Header Injection : By default (strictConnectionProcessing: false), the Connection header is ignored to prevent attackers from manipulating which headers are removed. Only enable strict processing in trusted environments.
Credentials Exposure : Be careful when forwarding request headers with ...c.req.header(). This may expose credentials like Authorization or Cookie to the target server. Explicitly control which headers are forwarded.
Open Proxy : Avoid creating an open proxy that allows users to proxy to arbitrary URLs. Always validate or restrict destination URLs.
Validation Example
Restrict proxy destinations for security:
import { proxy } from 'hono/proxy'
const ALLOWED_HOSTS = [ 'api.example.com' , 'cdn.example.com' ]
app . get ( '/proxy/:host/:path' , async ( c ) => {
const host = c . req . param ( 'host' )
const path = c . req . param ( 'path' )
if ( ! ALLOWED_HOSTS . includes ( host )) {
return c . json ({ error: 'Host not allowed' }, 403 )
}
return proxy ( `https:// ${ host } / ${ path } ` )
})
Error Handling
Handle fetch errors gracefully:
import { proxy } from 'hono/proxy'
app . get ( '/proxy/:path' , async ( c ) => {
try {
return await proxy ( `http:// ${ originServer } / ${ c . req . param ( 'path' ) } ` )
} catch ( error ) {
console . error ( 'Proxy error:' , error )
return c . json ({ error: 'Proxy failed' }, 502 )
}
})
Type Definitions
interface ProxyRequestInit extends Omit < RequestInit , 'headers' > {
raw ?: Request
headers ?:
| HeadersInit
| [ string , string ][]
| Record < string , string | undefined >
customFetch ?: ( request : Request ) => Promise < Response >
strictConnectionProcessing ?: boolean
}
function proxy (
input : string | URL | Request ,
init ?: ProxyRequestInit
) : Promise < Response >
Best Practices
Validate Destinations Always validate or allowlist destination URLs to prevent open proxy vulnerabilities
Control Headers Explicitly specify which headers to forward rather than forwarding all headers
Remove Credentials Set sensitive headers like Authorization to undefined when they shouldn’t be forwarded
Handle Errors Wrap proxy calls in try-catch blocks and return appropriate error responses
The proxy helper returns a fully-formed Response object that can be returned directly from your handler or modified before returning.