Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,7 @@ async function createServerConfig(
headers: serverOptions.headers,
// Disable the websocket if live reload is disabled (false/undefined are the only valid values)
ws: serverOptions.liveReload === false && serverOptions.hmr === false ? false : undefined,
// When server-side rendering (SSR) is enabled togather with SSL and Express is being used,
// we must configure Vite to use HTTP/1.1.
// This is necessary because Express does not support HTTP/2.
// We achieve this by defining an empty proxy.
// See: https://github.com/vitejs/vite/blob/c4b532cc900bf988073583511f57bd581755d5e3/packages/vite/src/node/http.ts#L106
proxy:
serverOptions.ssl && ssrMode === ServerSsrMode.ExternalSsrMiddleware ? (proxy ?? {}) : proxy,
proxy,
cors: {
// This will add the header `Access-Control-Allow-Origin: http://example.com`,
// where `http://example.com` is the requesting origin.
Expand Down
12 changes: 12 additions & 0 deletions packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ export function createAngularServerSideSSLPlugin(): Plugin {
return;
}

if (httpServer && 'ALPNProtocols' in httpServer) {
// Force Vite to use HTTP/1.1 when SSR and SSL are enabled.
// This is required because the Express server used for SSR does not support HTTP/2.
// See: https://github.com/vitejs/vite/blob/46d3077f2b63771cc50230bc907c48f5773c00fb/packages/vite/src/node/http.ts#L126

// We directly set the `ALPNProtocols` on the HTTP server to override the default behavior.
// Passing `ALPNProtocols` in the TLS options would cause Node.js to automatically include `h2`.
// Additionally, using `ALPNCallback` is not an option as it is mutually exclusive with `ALPNProtocols`.
// See: https://github.com/nodejs/node/blob/b8b4350ed3b73d225eb9e628d69151df56eaf298/lib/internal/http2/core.js#L3351
httpServer.ALPNProtocols = ['http/1.1'];
}

// TODO(alanagius): Replace `undici` with `tls.setDefaultCACertificates` once we only support Node.js 22.18.0+ and 24.5.0+.
// See: https://nodejs.org/api/tls.html#tlssetdefaultcacertificatescerts
const { getGlobalDispatcher, setGlobalDispatcher, Agent } = await import('undici');
Expand Down
17 changes: 13 additions & 4 deletions tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,23 @@ export default async function () {

const port = await ngServe('--ssl');

// Verify the server is running and the API response is correct.
await validateResponse('/main.js', /bootstrapApplication/);
await validateResponse('/home', /home works/);
// http 2
await validateResponse('/main.js', /bootstrapApplication/, true);
await validateResponse('/home', /home works/, true);

async function validateResponse(pathname: string, match: RegExp): Promise<void> {
// http 1.1
await validateResponse('/main.js', /bootstrapApplication/, false);
await validateResponse('/home', /home works/, false);

async function validateResponse(
pathname: string,
match: RegExp,
allowH2: boolean,
): Promise<void> {
const response = await fetch(new URL(pathname, `https://localhost:${port}`), {
dispatcher: new Agent({
connect: {
allowH2,
rejectUnauthorized: false,
},
}),
Expand Down