Nitro proxy SSL and CA certificate validation during SSR fetch
0 reputation · 08 Jan 2021, 10:05 UTC
0 reputation · 08 Jan 2021, 10:05 UTC
When using Nuxt 3 with the Nitro engine, server-side requests initiated via useFetch rely on the Node.js runtime for certificate validation rather than the client's browser. While client-side requests utilize standard trust stores, the SSR process often fails when targeting internal APIs protected by private CAs.
There is currently uncertainty regarding how to consistently inject custom CA bundles into the Nitro server-side fetch logic through the nuxt.config.ts without utilizing global environment variables like NODE_TLS_REJECT_UNAUTHORIZED. Relying on global flags is problematic for production environments as it affects the security posture of the entire process.
What is the recommended approach to pass specific CA certificates to the Nitro engine for server-side proxy requests? Is there a native configuration option in Nuxt to define custom SSL agents specifically for proxied routes while maintaining strict certificate validation?
Use nitro.fetch.agent.https in nuxt.config.ts to inject a custom https.Agent loaded with your private CA bundle. This scopes trust to server-side fetch only, avoiding NODE_TLS_REJECT_UNAUTHORIZED=0. For route-specific proxies, return a $fetch instance with a dedicated agent from a custom proxy handler in routeRules.
./certs/internal-ca.pem.nuxt.config.ts, create the agent at build time and assign it:import { defineNuxtConfig } from 'nuxt/config'
import { readFileSync } from 'fs'
import { https } from 'node:https'
const caCerts = readFileSync('./certs/internal-ca.pem', 'utf-8')
const httpsAgent = new https.Agent({
ca: caCerts,
rejectUnauthorized: true
})
export default defineNuxtConfig({
nitro: {
fetch: {
agent: {
https: httpsAgent
}
}
}
})
All server-side useFetch/$fetch calls now validate against your private CA. Client-side requests remain on the browser trust store.
If only certain proxied routes require the private CA, define a custom proxy handler:
// server/utils/internal-fetch.ts
import { createFetch } from 'ofetch'
import { https } from 'node:https'
import { readFileSync } from 'fs'
const ca = readFileSync('./certs/internal-ca.pem', 'utf-8')
const agent = new https.Agent({ ca, rejectUnauthorized: true })
export const internalFetch = createFetch({
agent
})
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/api/internal/**': {
proxy: { to: 'https://internal-api/**' },
handler: (event) => internalFetch(event.node.req.url, { baseURL: 'https://internal-api' })
}
}
})
Note: The handler pattern above is illustrative; Nitro 3.x proxy handlers receive event and should return a response. Consult current Nitro docs for exact signature.
Set NODE_EXTRA_CA_CERTS=/app/certs/internal-ca.pem in the container/process environment. This augments Node's default trust store for the current process only—no code changes required. Verify with node -e "console.log(require('https').globalAgent.options.ca?.length)".
server/routes/health.get.ts fetching https://your-internal-api/health.UNABLE_TO_VERIFY_LEAF_SIGNATURE.openssl s_client -connect your-internal-api:443 -CAfile /path/to/ca.pem -verify_return_error to validate the chain independently.experimental.fetch (enabled by default). Older versions may need nitro.experimental.fetch: true.openssl x509 -inform der -in cert.crt -out cert.pem).readFileSync.routeRules may vary by Nitro version—verify against your installed version's types.What Nuxt/Nitro version are you running? The nitro.fetch.agent API stabilized in Nuxt 3.8; earlier versions may require experimental.fetch or a Nitro plugin approach.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 08 Jan 2021, 15:22 UTC
While Nitro handles SSR requests through Node.js, you can override its TLS trust store without global flags. Nuxt 3's Nitro exposes nitro.server.fetch.agent in nuxt.config.ts, allowing you to inject a custom TLSAgent with specific CA certificates.
Configure it like:
export default defineNuxtConfig({
nitro: {
server: {
fetch: {
agent: new (require('tls')).TLSAgent([
'-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----'
])
}
}
}
})This agent applies to all useFetch calls during SSR. For route-specific CA handling, create a server middleware plugin that dynamically sets nitro.server.fetch.agent based on the request URL before Nitro processes the fetch.
Verify with npm ls nitro to confirm Nitro 2.x+, as the agent option stabilized in Nuxt 3.0. Misconfiguration affects all SSR traffic, so test with both internal and external endpoints.