title: Angular Universal express server keeps loading and fails with ERR_EMPTY_RESPONSE
body: <p>I have an Angular app and I'm trying server-side rendering using Angular universal (<a href="https://angular.io/guide/universal" rel="noreferrer">https://angular.io/guide/universal</a>) and it doesn't seem to work. I bundle my app and run it through express, I hit <a href="http://localhost:4000" rel="noreferrer">http://localhost:4000</a> and it keeps loading until I see an ERR_EMPTY_RESPONSE from the browser</p>
  
  <p>I've tried pretty much everything but no luck! Any help would be much appreciated</p>
  
  <p><em>here are the details of my code</em></p>
  
  <p><strong>package.json</strong></p>
  
  <pre><code>{
    "name": "my-app",
    "version": "3.0.0",
    "author": "N/A",
    "description": "N/A",
    "scripts": {
      "ng": "ng",
      "start": "ng serve --port 8000 --host 0.0.0.0",
      "build": "ng build",
      "build:ssr": "npm run build:client-and-server-bundles &amp;&amp; npm run webpack:server",
      "serve:ssr": "node dist/server.js",
      "build:client-and-server-bundles": "ng build --prod &amp;&amp; ng run my-app:server",
      "webpack:server": "webpack --config webpack.server.config.js --progress --colors",
      "test": "ng test",
      "lint": "tslint ./src/**/*.ts -t verbose",
      "e2e": "ng e2e"
    },
    "private": true,
    "dependencies": {
      "@agm/core": "^1.0.0-beta.5",
      "@angular/animations": "^7.2.0",
      "@angular/common": "^7.2.0",
      "@angular/compiler": "^7.2.0",
      "@angular/core": "^7.2.0",
      "@angular/forms": "^7.2.0",
      "@angular/http": "^7.2.0",
      "@angular/platform-browser": "^7.2.0",
      "@angular/platform-browser-dynamic": "^7.2.0",
      "@angular/router": "^7.2.0",
      "@angular/upgrade": "^7.0.0",
      "@nguniversal/common": "^6.0.0",
      "@nguniversal/express-engine": "^7.0.0",
      "@nguniversal/module-map-ngfactory-loader": "^7.1.0",
      "@types/jquery": "^3.3.28",
      "@types/swiper": "^4.4.1",
      "angular-in-memory-web-api": "^0.6.0",
      "angular2-text-mask": "^9.0.0",
      "bootstrap": "^4.1.3",
      "core-js": "^2.5.4",
      "express": "^4.16.4",
      "fullcalendar": "^3.10.0",
      "moment": "^2.23.0",
      "ng-fullcalendar": "^1.7.1",
      "ngx-google-places-autocomplete": "^2.0.3",
      "ngx-infinite-scroll": "^7.0.1",
      "ngx-slick": "^0.2.1",
      "reflect-metadata": "^0.1.10",
      "replace-in-file": "^3.4.3",
      "rxjs": "^6.3.3",
      "swiper": "^4.4.6",
      "zone.js": "^0.8.27"
    },
    "devDependencies": {
      "@angular-devkit/build-angular": "~0.12.1",
      "@angular/cli": "~7.2.1",
      "@angular/compiler-cli": "^7.2.0",
      "@angular/language-service": "^7.2.0",
      "@angular/platform-server": "^7.2.4",
      "@compodoc/compodoc": "^1.1.7",
      "@types/jasmine": "~3.3.4",
      "@types/jasminewd2": "~2.0.6",
      "@types/node": "~10.12.17",
      "codelyzer": "~4.5.0",
      "jasmine-core": "~3.3.0",
      "jasmine-spec-reporter": "~4.2.1",
      "karma": "^3.1.4",
      "karma-chrome-launcher": "~2.2.0",
      "karma-coverage-istanbul-reporter": "~2.0.4",
      "karma-jasmine": "~2.0.1",
      "karma-jasmine-html-reporter": "^1.4.0",
      "protractor": "~5.4.1",
      "ts-node": "~7.0.1",
      "tslint": "^5.12.1",
      "typescript": "~3.2.2",
      "webpack-cli": "^3.2.3",
      "karma-phantomjs-launcher": "^1.0.2",
      "lodash": "^4.16.2",
      "phantomjs-prebuilt": "^2.1.7",
      "ts-loader": "^4.5.0"
    }
  }
  
  </code></pre>
  
  <p><strong>src/app/app.module.ts</strong></p>
  
  <pre><code>import { NgModule } from '@angular/core';
  import { HttpClientModule } from '@angular/common/http';
  import { CommonModule } from '@angular/common';
  import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
  import { TransferHttpCacheModule } from '@nguniversal/common';
  
  // Modules
  import { CoreModule } from './core/core.module';
  import { SharedModule } from './shared/shared.module';
  import { ConfigModule } from './configs/config.module';
  
  // Routing
  import { AppRoutingModule } from './app-routing.module';
  
  // Components
  import { AppComponent } from './app.component';
  
  
  @NgModule({
    declarations: [
      AppComponent
    ],
    imports: [
      BrowserModule.withServerTransition({appId: 'my-app'}),
      BrowserTransferStateModule,
      TransferHttpCacheModule,
      CommonModule,
      AppRoutingModule,
      HttpClientModule,
      CoreModule,
      SharedModule,
      ConfigModule
    ],
    providers: [],
    bootstrap: [AppComponent]
  })
  export class AppModule {}
  
  </code></pre>
  
  <p><strong>src/app/app.server.module.ts</strong></p>
  
  <pre><code>import { NgModule } from '@angular/core';
  import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
  import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
  
  import { AppModule } from './app.module';
  import { AppComponent } from './app.component';
  
  
  @NgModule({
    imports: [
      AppModule,
      ServerModule,
      ModuleMapLoaderModule,
      ServerTransferStateModule
    ],
    providers: [],
    bootstrap: [AppComponent],
  })
  export class AppServerModule {}
  
  </code></pre>
  
  <p><strong>main.server.ts</strong></p>
  
  <pre><code>export { AppServerModule } from './app/app.server.module';
  
  </code></pre>
  
  <p><strong>tsconfig.server.json</strong></p>
  
  <pre><code>{
    "extends": "../tsconfig.json",
    "compilerOptions": {
      "module": "commonjs",
      "types": []
    },
    "exclude": [
      "test.ts",
      "**/*.spec.ts"
    ],
    "angularCompilerOptions": {
      "entryModule": "app/app.server.module#AppServerModule"
    }
  }
  
  </code></pre>
  
  <p><strong>angular.json</strong></p>
  
  <pre><code>...
       "server" : {
            "builder": "@angular-devkit/build-angular:server",
            "options": {
              "outputPath": "dist/server",
              "main": "src/main.server.ts",
              "tsConfig": "src/tsconfig.server.json",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false
            }
          }
  ...
  </code></pre>
  
  <p>After these changes, I was able to bundle the browser, server distributions successfully using <code>ng build --prod &amp;&amp; ng run my-app:server</code></p>
  
  <p>here's my <strong>server.ts</strong></p>
  
  <pre><code>// These are important and needed before anything else
  import 'zone.js/dist/zone-node';
  import 'reflect-metadata';
  
  import { enableProdMode } from '@angular/core';
  import { ngExpressEngine } from '@nguniversal/express-engine';
  
  import * as express from 'express';
  import { join } from 'path';
  
  const DIST_FOLDER = join(process.cwd(), 'dist');
  
  // Faster server renders w/ Prod mode (dev mode never needed)
  enableProdMode();
  
  const domino = require('domino');
  const fs = require('fs');
  const path = require('path');
  const template = fs.readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString();
  const win = domino.createWindow(template);
  
  global['window'] = win;
  global['document'] = win.document;
  global['DOMTokenList'] = win.DOMTokenList;
  global['Node'] = win.Node;
  global['Text'] = win.Text;
  global['HTMLElement'] = win.HTMLElement;
  global['navigator'] = win.navigator;
  global['CSS'] = null;
  global['Event'] = win.Event;
  global['Event']['prototype'] = win.Event.prototype;
  
  Object.defineProperty(win.document.body.style, 'transform', {
    value: () =&gt; {
      return {
        enumerable: true,
        configurable: true
      };
    },
  });
  
  // Express server
  const app = express();
  
  const PORT = process.env.PORT || 4000;
  
  const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
  const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
  
  app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
      provideModuleMap(LAZY_MODULE_MAP)
    ]
  }));
  
  app.set('view engine', 'html');
  app.set('views', join(DIST_FOLDER, 'browser'));
  
  // Server static files from /browser
  app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
  
  // All regular routes use the Universal engine
  app.get('*', (req, res) =&gt; {
    res.render('index', { req });
  });
  
  // Start up the Node server
  app.listen(PORT, () =&gt; {
    console.log(`Node server listening on http://localhost:${PORT}`);
  });
  
  </code></pre>
  
  <p>and <strong>webpack.server.config.js</strong></p>
  
  <pre><code>const path = require('path');
  const webpack = require('webpack');
  
  module.exports = {
    entry: { server: './server.ts' },
    resolve: { extensions: ['.js', '.ts'] },
    target: 'node',
    mode: 'none',
    // this makes sure we includes node_modules and other 3rd party libraries
    externals: [/(node_modules|main(\\|\/)..*(\\|\/).js)/],
    output: {
      path: path.join(__dirname, 'dist'),
      filename: '[name].js'
    },
    module: {
      rules: [
        { test: /\.ts$/, loader: 'ts-loader' },
        {
          // Mark files inside `@angular/core` as using SystemJS style dynamic imports.
          // Removing this will cause deprecation warnings to appear.
          test: /(\\|\/)@angular(\\|\/)core(\\|\/).+\.js$/,
          parser: { system: true },
        },
      ]
    },
    plugins: [
      // Temporary Fix for issue: https://github.com/angular/angular/issues/11580
      // for 'WARNING Critical dependency: the request of a dependency is an expression'
      new webpack.ContextReplacementPlugin(
        /(.+)?angular(\\|\/)core(.+)?/,
        path.join(__dirname, 'src'), // location of your src
        {} // a map of your routes
      ),
      new webpack.ContextReplacementPlugin(
        /(.+)?express(\\|\/)(.+)?/,
        path.join(__dirname, 'src'),
        {}
      ),
      new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery",
        "window.jQuery": "jquery"
      })
    ]
  };
  
  </code></pre>
  
  <p>npm run build:ssr &amp;&amp; npm run serve:ssr -> <a href="http://localhost:4000" rel="noreferrer">http://localhost:4000</a> -> keeps loading -> ERR_EMPTY_RESPONSE</p>