next.js mit Apache und Let’s Encrypt https Zertifikat einrichten

Durch einen Podcast (Revision 403: Meta-Frameworks | Working Draft) bin ich auf ein JavaScript Framework gestoßen, dass mein Interesse geweckt hat. es heißt next.js und ermöglicht serverseitig gerenderte React-Anwendungen bei der jede Webseite als separate Funktion erstellt wird.

Eine Google Sucht bringt z.B. folgende Ergebnisse ans Tageslicht:

Dazwischen finden sich auch reichlich Tutorials, Tipps und FAQ Seiten die next.js von allen Seiten beleuchten. Natürlich habe ich mir next.js auch mal angesehen und meine ersten Versuche mit dem React Framework gemacht von dem alls so schwärmen.

Next.js Installation

Laut der Dokumentation reicht es schon den Befehl “npx create-next-app” auf der Konsole aufzurufen und schon hat man eine fertige next.js Applikation mit der man starten kann.

Das Tool npx ist seit Version 5.20 Teil der npm Installation das dazu dient node.js Pakete herunterzuladen und auszuführen.

 ~ npx 

 Führt Programme aus, die von npm Paketen bereitgestellt werden.

   npx [Optionen] <Befehl>[@Version] [Befehlsargument]...
   npx [Optionen] [-p|--package <Paket>]... <Befehl> [Befehlsargument]...
   npx [Optionen] -c '<Befehlszeichenkette>'
   npx --shell-auto-fallback [Shell]

 Optionen:
   --package, -p          Das zu installierende Paket.                   [string]
   --cache                Ort des npm Zwischenspeichers.                 [string]
   --always-spawn         Always spawn a child process to execute the command.
                                                                        [boolean]
   --no-install           Überspringe die Installation, falls ein Paket fehlt.
                                                                        [boolean]
   --userconfig           Pfad zu npmrc im Benutzerverzeichnis.          [string]
   --call, -c             Führe Zeichenkette aus, als wäre sie innerhalb von `npm
                          run-script`.                                   [string]
   --shell, -s            Shell, mit der Programme ausgeführt werden soll, wenn
                          überhaupt.                   [string] [Standard: false]
   --shell-auto-fallback  Erzeuge Shellcode, um npx als Alternative zu "Programm
                          konnte nicht gefunden werden" zu benutzen.
                              [string] [Möglichkeiten: "", "bash", "fish", "zsh"]
   --ignore-existing      Ignoriere bestehende Programme innerhalb von $PATH oder
                          im lokalen Projekt. Dies zwingt npx dazu, die neuste
                          Version herunterzuladen und zu benutzen.      [boolean]
   --quiet, -q            Unterdrücke Output von npx. Unterbefehle sind nicht
                          davon betroffen.                              [boolean]
   --npm                  npm-Programm für die interne Benutzung.
            [string] [Standard: "/usr/local/lib/node_modules/npm/bin/npm-cli.js"]
   --node-arg, -n         Extra node Argument, wenn eine node ausführbare
                          Binärdatei gerufen ist.                        [string]
   --version, -v          Version anzeigen                              [boolean]
   --help, -h             Hilfe anzeigen                                [boolean]
 
 In der Manpage npx(1) ist die gesamte Dokumentation einzusehen. 

Der npx Befehl “create-next-app” erstellt dann (ähnlich wie der Befehl “npx create-react-app”) eine next.js app.

npx create-next-app

Wenn man npx create-next-app (ohne Argumente) ausführt, wird man nach dem Projektnamen gefragt (der kleine Großbuchstaben enthalten darf). Keine Ahnung warum nur lowerces erlaubt ist, aber was solls.
Danach startet der Download diverser Komponenten und die App wird eingerichtet. Das sieht dann wie folgt aus:

  /projects/next.js npx create-next-app
 npx: Installierte 1 in 1.176s
 ✔ What is your project named? … test-app-1
 Creating a new Next.js app in /projects/next.js/test-app-1.
 

 Installing react, react-dom, and next using yarn...
 

 yarn add v1.12.3
 info No lockfile found.
 [1/4] Resolving packages...
 warning next > @babel/runtime-corejs2 > core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
 warning next > styled-jsx > babel-types > babel-runtime > core-js@2.6.11: core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.
 [2/4] Fetching packages...
 info fsevents@2.1.2: The platform "linux" is incompatible with this module.
 info "fsevents@2.1.2" is an optional dependency and failed compatibility check. Excluding it from installation.
 info fsevents@2.1.2: The engine "node" is incompatible with this module. Expected version "^8.16.0 || ^10.6.0 || >=11.0.0". Got "8.11.3"
 info fsevents@1.2.11: The platform "linux" is incompatible with this module.
 info "fsevents@1.2.11" is an optional dependency and failed compatibility check. Excluding it from installation.
 [3/4] Linking dependencies...
 warning "next > native-url@0.2.3" has unmet peer dependency "@types/node@>=8.0.0".
 [4/4] Building fresh packages...
 success Saved lockfile.
 warning Your current version of Yarn is out of date. The latest version is "1.21.1", while you're on "1.12.3".
 info To upgrade, run the following command:
 $ sudo apt-get update && sudo apt-get install yarn
 success Saved 510 new dependencies.
 info Direct dependencies
 ├─ next@9.1.5
 ├─ react-dom@16.12.0
 └─ react@16.12.0
 info All dependencies
 ├─ @ampproject/toolbox-optimizer@1.1.1
 ├─ @ampproject/toolbox-runtime-version@1.1.1
 ├─ @ampproject/toolbox-script-csp@1.1.1
 ├─ @babel/code-frame@7.5.5
 ├─ @babel/core@7.7.2
 ├─ @babel/generator@7.7.4
 ├─ @babel/helper-builder-binary-assignment-operator-visitor@7.7.4
 ├─ @babel/helper-builder-react-jsx@7.7.4
 ├─ @babel/helper-call-delegate@7.7.4
 ├─ @babel/helper-create-class-features-plugin@7.7.4
 ├─ @babel/helper-define-map@7.7.4
 ├─ @babel/helper-explode-assignable-expression@7.7.4
 ├─ @babel/helper-module-transforms@7.7.5
 ├─ @babel/helper-regex@7.5.5
 ├─ @babel/helper-wrap-function@7.7.4
 ├─ @babel/helpers@7.7.4
 ├─ @babel/highlight@7.5.0
 ├─ @babel/plugin-proposal-async-generator-functions@7.7.4
 ├─ @babel/plugin-proposal-class-properties@7.7.0
 ├─ @babel/plugin-proposal-dynamic-import@7.7.4
 ├─ @babel/plugin-proposal-json-strings@7.7.4
 ├─ @babel/plugin-proposal-nullish-coalescing-operator@7.7.4
 ├─ @babel/plugin-proposal-object-rest-spread@7.6.2
 ├─ @babel/plugin-proposal-optional-catch-binding@7.7.4
 ├─ @babel/plugin-proposal-optional-chaining@7.7.4
 ├─ @babel/plugin-proposal-unicode-property-regex@7.7.4
 ├─ @babel/plugin-syntax-async-generators@7.7.4
 ├─ @babel/plugin-syntax-dynamic-import@7.7.4
 ├─ @babel/plugin-syntax-json-strings@7.7.4
 ├─ @babel/plugin-syntax-nullish-coalescing-operator@7.7.4
 ├─ @babel/plugin-syntax-object-rest-spread@7.7.4
 ├─ @babel/plugin-syntax-optional-catch-binding@7.7.4
 ├─ @babel/plugin-syntax-optional-chaining@7.7.4
 ├─ @babel/plugin-syntax-top-level-await@7.7.4
 ├─ @babel/plugin-syntax-typescript@7.7.4
 ├─ @babel/plugin-transform-arrow-functions@7.7.4
 ├─ @babel/plugin-transform-async-to-generator@7.7.4
 ├─ @babel/plugin-transform-block-scoped-functions@7.7.4
 ├─ @babel/plugin-transform-block-scoping@7.7.4
 ├─ @babel/plugin-transform-classes@7.7.4
 ├─ @babel/plugin-transform-computed-properties@7.7.4
 ├─ @babel/plugin-transform-destructuring@7.7.4
 ├─ @babel/plugin-transform-dotall-regex@7.7.4
 ├─ @babel/plugin-transform-duplicate-keys@7.7.4
 ├─ @babel/plugin-transform-exponentiation-operator@7.7.4
 ├─ @babel/plugin-transform-for-of@7.7.4
 ├─ @babel/plugin-transform-function-name@7.7.4
 ├─ @babel/plugin-transform-literals@7.7.4
 ├─ @babel/plugin-transform-member-expression-literals@7.7.4
 ├─ @babel/plugin-transform-modules-amd@7.7.5
 ├─ @babel/plugin-transform-modules-commonjs@7.7.0
 ├─ @babel/plugin-transform-modules-systemjs@7.7.4
 ├─ @babel/plugin-transform-modules-umd@7.7.4
 ├─ @babel/plugin-transform-named-capturing-groups-regex@7.7.4
 ├─ @babel/plugin-transform-new-target@7.7.4
 ├─ @babel/plugin-transform-object-super@7.7.4
 ├─ @babel/plugin-transform-parameters@7.7.4
 ├─ @babel/plugin-transform-property-literals@7.7.4
 ├─ @babel/plugin-transform-react-display-name@7.7.4
 ├─ @babel/plugin-transform-react-jsx-self@7.7.4
 ├─ @babel/plugin-transform-react-jsx-source@7.7.4
 ├─ @babel/plugin-transform-react-jsx@7.7.4
 ├─ @babel/plugin-transform-regenerator@7.7.5
 ├─ @babel/plugin-transform-reserved-words@7.7.4
 ├─ @babel/plugin-transform-runtime@7.6.2
 ├─ @babel/plugin-transform-shorthand-properties@7.7.4
 ├─ @babel/plugin-transform-spread@7.7.4
 ├─ @babel/plugin-transform-sticky-regex@7.7.4
 ├─ @babel/plugin-transform-template-literals@7.7.4
 ├─ @babel/plugin-transform-typeof-symbol@7.7.4
 ├─ @babel/plugin-transform-typescript@7.7.4
 ├─ @babel/plugin-transform-unicode-regex@7.7.4
 ├─ @babel/preset-env@7.7.1
 ├─ @babel/preset-modules@0.1.1
 ├─ @babel/preset-react@7.7.0
 ├─ @babel/preset-typescript@7.7.2
 ├─ @babel/runtime-corejs2@7.7.2
 ├─ @babel/runtime@7.7.2
 ├─ @webassemblyjs/floating-point-hex-parser@1.8.5
 ├─ @webassemblyjs/helper-code-frame@1.8.5
 ├─ @webassemblyjs/helper-fsm@1.8.5
 ├─ @webassemblyjs/helper-wasm-section@1.8.5
 ├─ @webassemblyjs/wasm-edit@1.8.5
 ├─ @webassemblyjs/wasm-opt@1.8.5
 ├─ @xtuc/ieee754@1.2.0
 ├─ accepts@1.3.7
 ├─ acorn@6.4.0
 ├─ ajv-errors@1.0.1
 ├─ ajv-keywords@3.4.1
 ├─ ajv@6.10.2
 ├─ amphtml-validator@1.0.23
 ├─ ansi-colors@3.2.4
 ├─ ansi-html@0.0.7
 ├─ ansi-styles@3.2.1
 ├─ anymatch@3.1.1
 ├─ aproba@1.2.0
 ├─ argparse@1.0.10
 ├─ arr-flatten@1.1.0
 ├─ array-union@1.0.2
 ├─ array-uniq@1.0.3
 ├─ asap@2.0.6
 ├─ asn1.js@4.10.1
 ├─ assert@1.5.0
 ├─ assign-symbols@1.0.0
 ├─ async-each@1.0.3
 ├─ async-retry@1.2.3
 ├─ async-sema@3.0.0
 ├─ atob@2.1.2
 ├─ autodll-webpack-plugin@0.4.2
 ├─ autoprefixer@9.7.3
 ├─ babel-code-frame@6.26.0
 ├─ babel-core@7.0.0-bridge.0
 ├─ babel-loader@8.0.6
 ├─ babel-plugin-transform-define@2.0.0
 ├─ babel-plugin-transform-react-remove-prop-types@0.4.24
 ├─ babel-runtime@6.26.0
 ├─ babel-types@6.26.0
 ├─ base@0.11.2
 ├─ base64-js@1.3.1
 ├─ big.js@5.2.2
 ├─ binary-extensions@2.0.0
 ├─ bluebird@3.7.2
 ├─ brace-expansion@1.1.11
 ├─ braces@2.3.2
 ├─ browserify-aes@1.2.0
 ├─ browserify-cipher@1.0.1
 ├─ browserify-des@1.0.2
 ├─ browserify-sign@4.0.4
 ├─ browserify-zlib@0.2.0
 ├─ browserslist@4.8.2
 ├─ buffer-xor@1.0.3
 ├─ buffer@4.9.2
 ├─ builtin-status-codes@3.0.0
 ├─ bytes@3.0.0
 ├─ cacache@12.0.3
 ├─ cache-base@1.0.1
 ├─ caller-callsite@2.0.0
 ├─ caller-path@2.0.0
 ├─ callsites@2.0.0
 ├─ camelcase@5.3.1
 ├─ caniuse-lite@1.0.30001015
 ├─ chalk@2.4.2
 ├─ chokidar@3.3.0
 ├─ chownr@1.1.3
 ├─ chrome-trace-event@1.0.2
 ├─ ci-info@2.0.0
 ├─ cipher-base@1.0.4
 ├─ class-utils@0.3.6
 ├─ cli-cursor@2.1.0
 ├─ cli-spinners@2.2.0
 ├─ clone@1.0.4
 ├─ collection-visit@1.0.0
 ├─ color-convert@1.9.3
 ├─ color-name@1.1.3
 ├─ colors@1.1.2
 ├─ commander@2.9.0
 ├─ compressible@2.0.17
 ├─ compression@1.7.4
 ├─ concat-map@0.0.1
 ├─ concat-stream@1.6.2
 ├─ conf@5.0.0
 ├─ console-browserify@1.2.0
 ├─ constants-browserify@1.0.0
 ├─ content-type@1.0.4
 ├─ convert-source-map@1.7.0
 ├─ cookie@0.4.0
 ├─ copy-concurrently@1.0.5
 ├─ copy-descriptor@0.1.1
 ├─ core-js-compat@3.5.0
 ├─ core-js@2.6.11
 ├─ core-util-is@1.0.2
 ├─ cosmiconfig@5.2.1
 ├─ create-ecdh@4.0.3
 ├─ create-hmac@1.1.7
 ├─ crypto-browserify@3.12.0
 ├─ css-blank-pseudo@0.1.4
 ├─ css-has-pseudo@0.10.0
 ├─ css-loader@3.2.0
 ├─ css-prefers-color-scheme@3.1.1
 ├─ css@2.2.4
 ├─ cssdb@4.4.0
 ├─ cssnano-preset-simple@1.0.1
 ├─ cssnano-simple@1.0.0
 ├─ cyclist@1.0.1
 ├─ debug@2.6.9
 ├─ decode-uri-component@0.2.0
 ├─ defaults@1.0.3
 ├─ define-properties@1.1.3
 ├─ del@3.0.0
 ├─ des.js@1.0.1
 ├─ destroy@1.0.4
 ├─ devalue@2.0.1
 ├─ diffie-hellman@5.0.3
 ├─ domain-browser@1.2.0
 ├─ dot-prop@5.2.0
 ├─ duplexify@3.7.1
 ├─ ee-first@1.1.1
 ├─ electron-to-chromium@1.3.322
 ├─ emojis-list@2.1.0
 ├─ encodeurl@1.0.2
 ├─ enhanced-resolve@4.1.1
 ├─ env-paths@2.2.0
 ├─ errno@0.1.7
 ├─ error-ex@1.3.2
 ├─ escape-html@1.0.3
 ├─ escape-string-regexp@1.0.5
 ├─ eslint-scope@4.0.3
 ├─ esprima@4.0.1
 ├─ esrecurse@4.2.1
 ├─ estraverse@4.3.0
 ├─ esutils@2.0.3
 ├─ etag@1.8.1
 ├─ events@3.0.0
 ├─ expand-brackets@2.1.4
 ├─ extglob@2.0.4
 ├─ fast-deep-equal@2.0.1
 ├─ fast-json-stable-stringify@2.1.0
 ├─ file-loader@4.2.0
 ├─ fill-range@4.0.0
 ├─ find-cache-dir@2.1.0
 ├─ flatten@1.0.3
 ├─ flush-write-stream@1.1.1
 ├─ for-in@1.0.2
 ├─ fork-ts-checker-webpack-plugin@3.1.1
 ├─ from2@2.3.0
 ├─ fs.realpath@1.0.0
 ├─ glob-parent@5.1.0
 ├─ glob-to-regexp@0.4.1
 ├─ glob@7.1.6
 ├─ globby@6.1.0
 ├─ graceful-readlink@1.0.1
 ├─ has-ansi@2.0.0
 ├─ has-symbols@1.0.1
 ├─ has-value@1.0.0
 ├─ has-values@1.0.0
 ├─ has@1.0.3
 ├─ hash.js@1.1.7
 ├─ hmac-drbg@1.0.1
 ├─ hosted-git-info@2.8.5
 ├─ html-entities@1.2.1
 ├─ http-errors@1.7.2
 ├─ https-browserify@1.0.0
 ├─ iconv-lite@0.4.24
 ├─ icss-utils@4.1.1
 ├─ ieee754@1.1.13
 ├─ ignore-loader@0.1.2
 ├─ import-cwd@2.1.0
 ├─ import-fresh@2.0.0
 ├─ import-from@2.1.0
 ├─ infer-owner@1.0.4
 ├─ inflight@1.0.6
 ├─ invariant@2.2.4
 ├─ is-accessor-descriptor@1.0.0
 ├─ is-arrayish@0.2.1
 ├─ is-binary-path@2.1.0
 ├─ is-data-descriptor@1.0.0
 ├─ is-descriptor@1.0.2
 ├─ is-directory@0.3.1
 ├─ is-docker@2.0.0
 ├─ is-extglob@2.1.1
 ├─ is-glob@4.0.1
 ├─ is-obj@2.0.0
 ├─ is-path-cwd@1.0.0
 ├─ is-path-in-cwd@1.0.1
 ├─ is-path-inside@1.0.1
 ├─ is-plain-obj@1.1.0
 ├─ is-plain-object@2.0.4
 ├─ is-windows@1.0.2
 ├─ is-wsl@2.1.1
 ├─ isarray@1.0.0
 ├─ jest-worker@24.9.0
 ├─ js-levenshtein@1.1.6
 ├─ js-tokens@4.0.0
 ├─ js-yaml@3.13.1
 ├─ jsesc@2.5.2
 ├─ json-parse-better-errors@1.0.2
 ├─ json-schema-traverse@0.4.1
 ├─ json-schema-typed@7.0.3
 ├─ json5@2.1.1
 ├─ kind-of@3.2.2
 ├─ launch-editor@2.2.1
 ├─ load-json-file@2.0.0
 ├─ loader-runner@2.4.0
 ├─ locate-path@3.0.0
 ├─ lodash.template@4.5.0
 ├─ lodash.templatesettings@4.2.0
 ├─ log-symbols@2.2.0
 ├─ loose-envify@1.4.0
 ├─ lru-cache@5.1.1
 ├─ mamacro@0.0.3
 ├─ map-visit@1.0.0
 ├─ merge-stream@2.0.0
 ├─ microevent.ts@0.1.1
 ├─ miller-rabin@4.0.1
 ├─ mime-db@1.42.0
 ├─ mime-types@2.1.25
 ├─ mime@1.6.0
 ├─ mimic-fn@1.2.0
 ├─ mini-css-extract-plugin@0.8.0
 ├─ minimalistic-assert@1.0.1
 ├─ minimalistic-crypto-utils@1.0.1
 ├─ minimatch@3.0.4
 ├─ minimist@1.2.0
 ├─ mississippi@3.0.0
 ├─ mixin-deep@1.3.2
 ├─ move-concurrently@1.0.1
 ├─ ms@2.1.2
 ├─ nanomatch@1.2.13
 ├─ native-url@0.2.3
 ├─ negotiator@0.6.2
 ├─ next@9.1.5
 ├─ node-libs-browser@2.2.1
 ├─ node-releases@1.1.42
 ├─ normalize-package-data@2.5.0
 ├─ normalize-range@0.1.2
 ├─ normalize-url@1.9.1
 ├─ num2fraction@1.2.2
 ├─ object-assign@4.1.1
 ├─ object-copy@0.1.0
 ├─ object-keys@1.1.1
 ├─ on-finished@2.3.0
 ├─ on-headers@1.0.2
 ├─ onetime@2.0.1
 ├─ ora@3.4.0
 ├─ os-browserify@0.3.0
 ├─ p-limit@2.2.1
 ├─ p-locate@3.0.0
 ├─ p-map@1.2.0
 ├─ p-try@2.2.0
 ├─ pako@1.0.10
 ├─ parallel-transform@1.2.0
 ├─ parse-json@2.2.0
 ├─ parse5-htmlparser2-tree-adapter@5.1.0
 ├─ parse5@5.1.0
 ├─ pascalcase@0.1.1
 ├─ path-browserify@0.0.1
 ├─ path-dirname@1.0.2
 ├─ path-is-inside@1.0.2
 ├─ path-parse@1.0.6
 ├─ path-to-regexp@6.1.0
 ├─ path-type@2.0.0
 ├─ pinkie-promise@2.0.1
 ├─ pinkie@2.0.4
 ├─ pkg-dir@3.0.0
 ├─ pkg-up@3.1.0
 ├─ pnp-webpack-plugin@1.5.0
 ├─ posix-character-classes@0.1.1
 ├─ postcss-attribute-case-insensitive@4.0.1
 ├─ postcss-color-functional-notation@2.0.1
 ├─ postcss-color-gray@5.0.0
 ├─ postcss-color-hex-alpha@5.0.3
 ├─ postcss-color-mod-function@3.0.3
 ├─ postcss-color-rebeccapurple@4.0.1
 ├─ postcss-custom-media@7.0.8
 ├─ postcss-custom-properties@8.0.11
 ├─ postcss-custom-selectors@5.1.2
 ├─ postcss-dir-pseudo-class@5.0.0
 ├─ postcss-double-position-gradients@1.0.0
 ├─ postcss-env-function@2.0.2
 ├─ postcss-flexbugs-fixes@4.1.0
 ├─ postcss-focus-visible@4.0.0
 ├─ postcss-focus-within@3.0.0
 ├─ postcss-font-variant@4.0.0
 ├─ postcss-gap-properties@2.0.0
 ├─ postcss-image-set-function@3.0.1
 ├─ postcss-initial@3.0.2
 ├─ postcss-lab-function@2.0.1
 ├─ postcss-load-config@2.1.0
 ├─ postcss-loader@3.0.0
 ├─ postcss-logical@3.0.0
 ├─ postcss-media-minmax@4.0.0
 ├─ postcss-modules-extract-imports@2.0.0
 ├─ postcss-modules-local-by-default@3.0.2
 ├─ postcss-modules-scope@2.1.1
 ├─ postcss-modules-values@3.0.0
 ├─ postcss-nesting@7.0.1
 ├─ postcss-overflow-shorthand@2.0.0
 ├─ postcss-page-break@2.0.0
 ├─ postcss-place@4.0.1
 ├─ postcss-preset-env@6.7.0
 ├─ postcss-pseudo-class-any-link@6.0.0
 ├─ postcss-replace-overflow-wrap@3.0.0
 ├─ postcss-selector-matches@4.0.0
 ├─ postcss-selector-not@4.0.0
 ├─ postcss-value-parser@4.0.2
 ├─ prepend-http@1.0.4
 ├─ private@0.1.8
 ├─ process-nextick-args@2.0.1
 ├─ process@0.11.10
 ├─ promise-inflight@1.0.1
 ├─ promise@7.1.1
 ├─ prop-types-exact@1.2.0
 ├─ prop-types@15.7.2
 ├─ prr@1.0.1
 ├─ public-encrypt@4.0.3
 ├─ pump@3.0.0
 ├─ pumpify@1.5.1
 ├─ punycode@1.3.2
 ├─ query-string@4.3.4
 ├─ querystring-es3@0.2.1
 ├─ querystring@0.2.0
 ├─ randomfill@1.0.4
 ├─ range-parser@1.2.1
 ├─ raw-body@2.4.0
 ├─ react-dom@16.12.0
 ├─ react-error-overlay@5.1.6
 ├─ react-is@16.8.6
 ├─ react@16.12.0
 ├─ read-pkg@2.0.0
 ├─ readable-stream@2.3.6
 ├─ readdirp@3.2.0
 ├─ reflect.ownkeys@0.2.0
 ├─ regenerate-unicode-properties@8.1.0
 ├─ regenerator-transform@0.14.1
 ├─ regexpu-core@4.6.0
 ├─ regjsgen@0.5.1
 ├─ regjsparser@0.6.1
 ├─ remove-trailing-separator@1.1.0
 ├─ repeat-element@1.1.3
 ├─ resolve-url@0.2.1
 ├─ resolve@1.13.1
 ├─ restore-cursor@2.0.0
 ├─ ret@0.1.15
 ├─ retry@0.12.0
 ├─ ripemd160@2.0.2
 ├─ run-queue@1.0.3
 ├─ safer-buffer@2.1.2
 ├─ scheduler@0.18.0
 ├─ semver@5.7.1
 ├─ send@0.17.1
 ├─ serialize-javascript@2.1.2
 ├─ set-value@2.0.1
 ├─ setimmediate@1.0.5
 ├─ shell-quote@1.7.2
 ├─ signal-exit@3.0.2
 ├─ snapdragon-node@2.1.1
 ├─ snapdragon-util@3.0.1
 ├─ sort-keys@1.1.2
 ├─ source-list-map@2.0.1
 ├─ source-map-resolve@0.5.2
 ├─ source-map-support@0.5.16
 ├─ source-map-url@0.4.0
 ├─ spdx-correct@3.1.0
 ├─ spdx-exceptions@2.2.0
 ├─ split-string@3.1.0
 ├─ sprintf-js@1.0.3
 ├─ ssri@6.0.1
 ├─ static-extend@0.1.2
 ├─ stream-browserify@2.0.2
 ├─ stream-each@1.2.3
 ├─ stream-http@2.8.3
 ├─ strict-uri-encode@1.1.0
 ├─ string_decoder@1.1.1
 ├─ strip-ansi@3.0.1
 ├─ strip-bom@3.0.0
 ├─ style-loader@1.0.0
 ├─ styled-jsx@3.2.4
 ├─ stylis-rule-sheet@0.0.10
 ├─ stylis@3.5.4
 ├─ terser-webpack-plugin@1.4.3
 ├─ terser@4.4.2
 ├─ through2@2.0.5
 ├─ timers-browserify@2.0.11
 ├─ to-arraybuffer@1.0.1
 ├─ to-fast-properties@1.0.3
 ├─ to-object-path@0.3.0
 ├─ to-regex-range@2.1.1
 ├─ traverse@0.6.6
 ├─ ts-pnp@1.1.5
 ├─ tslib@1.10.0
 ├─ tty-browserify@0.0.0
 ├─ typedarray-to-buffer@3.1.5
 ├─ typedarray@0.0.6
 ├─ unfetch@4.1.0
 ├─ unicode-canonical-property-names-ecmascript@1.0.4
 ├─ unicode-match-property-ecmascript@1.0.4
 ├─ unicode-match-property-value-ecmascript@1.1.0
 ├─ unicode-property-aliases-ecmascript@1.0.5
 ├─ union-value@1.0.1
 ├─ unique-filename@1.1.1
 ├─ unique-slug@2.0.2
 ├─ unpipe@1.0.0
 ├─ unset-value@1.0.0
 ├─ upath@1.2.0
 ├─ uri-js@4.2.2
 ├─ url-polyfill@1.1.7
 ├─ url@0.11.0
 ├─ use-subscription@1.1.1
 ├─ use@3.1.1
 ├─ util-deprecate@1.0.2
 ├─ util@0.11.1
 ├─ uuid@3.3.3
 ├─ validate-npm-package-license@3.0.4
 ├─ vary@1.1.2
 ├─ vm-browserify@1.1.2
 ├─ watchpack@2.0.0-beta.5
 ├─ wcwidth@1.0.1
 ├─ webpack-dev-middleware@3.7.0
 ├─ webpack-hot-middleware@2.25.0
 ├─ webpack-log@2.0.0
 ├─ webpack-merge@4.2.2
 ├─ webpack-sources@1.4.3
 ├─ webpack@4.41.2
 ├─ whatwg-fetch@3.0.0
 ├─ worker-farm@1.7.0
 ├─ worker-rpc@0.1.1
 ├─ write-file-atomic@3.0.1
 ├─ xtend@4.0.2
 ├─ y18n@4.0.0
 └─ yallist@3.1.1
 Done in 8.89s.

 Initialized a git repository.

 Success! Created test-app-1 at /projects/next.js/test-app-1
 Inside that directory, you can run several commands:
 
   yarn dev
     Starts the development server.
 
   yarn build
     Builds the app for production.
 
   yarn start
     Runs the built app in production mode.
 
 We suggest that you begin by typing:
 
   cd test-app-1
   yarn dev
 
  /projects/next.js  

Im aktuellen Verzeichnis gibt es nun einen Unterordner mit dem Namen meiner App (test-app-1). In dem Verzeichnis liegt nun die frisch erstellte next.js App. Sogar ein fertiges git Repository ist schon eingerichtet und alles ist mit der commit message “Initial commit from Create Next App” eingecheckt – nicht schlecht.

Im Unterverzeichnis pages gibt es sogar schon eine index.js Datei, es ist also alles so weit vorbereitet für einen ersten Test.

 /projects/next.js/test-app-1 npm run dev
 
 > test-app-1@0.1.0 dev /projects/next.js/test-app-1
 > next dev
 
 [ wait ]  starting the development server ...
 [ info ]  waiting on http://localhost:3000 ...
 Port 3000 is already in use.
 npm ERR! code ELIFECYCLE
 npm ERR! errno 1
 npm ERR! test-app-1@0.1.0 dev: `next dev`
 npm ERR! Exit status 1
 npm ERR! 
 npm ERR! Failed at the test-app-1@0.1.0 dev script.
 npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
 
 npm ERR! A complete log of this run can be found in:
 npm ERR!     /root/.npm/_logs/2019-12-14T17_42_53_708Z-debug.log 

Pech. Next.js versucht einen Server auf Port 3000 zu starten und da läuft natürlich schon was anderes – also mein Fehler :-(. Nun also schnell den Port ändern. Mal sehen, ob das bei next.js so einfach möglich ist …

Next.js Port ändern

OK, so einfach geht das also. Einfach nur in der package.json die option “-p PORT” eintragen.

 {
   "name": "test-app-1",
   "version": "0.1.0",
   "private": true,
   "scripts": {
     "dev": "next dev -p 18080",
     "build": "next build",
     "start": "next start"
   },
   "dependencies": {
     "next": "9.1.5",
     "react": "16.12.0",
     "react-dom": "16.12.0"
   }
 } 

Nun sieht das Ergebnis schon viel besser aus.

  /projects/next.js/test-app-1 npm run dev
 
 > test-app-1@0.1.0 dev /projects/next.js/test-app-1
 > next dev -p 18080
 
 [ wait ]  starting the development server ...
 [ info ]  waiting on http://localhost:18080 ...
 [ ready ] compiled successfully - ready on http://localhost:18080 

Das Ergebnis im Browser sieht dann wie folgt aus:

Allerdings ist die Aussage “ready on http://localhost:18080” leider nicht ganz korrekt. denn ein kleiner Test beweist, dass die next.js App nicht nur auf der IP-Adresse von localhost (127.0.0.1) lauscht sondern auf allen verfügbaren IP-Adressen. Das kann schnell nach hinten los gehen, wenn man den next.js Service gar nicht ausserhalb des Servers erreichen soll. Hier müssen die next.js Entwickler evtl. noch ein wenig nachbessern.

 ~ sudo netstat -tulpn | grep LISTEN | grep 18080
 tcp6   0  0 :::18080    :::*   LISTEN   32498/node       

Eine weitere Anpassung der next.js package.json kann hier abhilfe schaffen.

  {
   "name": "test-app-1",
   "version": "0.1.0",
   "private": true,
   "scripts": {
     "dev": "next dev -p 18080 --hostname 127.0.0.1",
     "build": "next build",
     "start": "next start"
   },
   "dependencies": {
     "next": "9.1.5",
     "react": "16.12.0",
     "react-dom": "16.12.0"
   }
 } 

Nun sieht das Ergbnis schon viel besser aus.

  ~ sudo netstat -tulpn | grep LISTEN | grep 18080
 tcp   0  0 127.0.0.1:18080    0.0.0.0:*   LISTEN   1049/node        

Damit ist der Anfang gemacht. Eine next.js App, die auf localhost port 18080 lauscht.

Next.js in den Apache einbinden

Web-Applikationen laufen idealerweise nun mal auf Port 80 und nicht auf Port 3000 und schon garnicht auf Port 18080. Daher müsste man eigentlich den Port 80 in die next.js package.json Datei eintragen. Aber Auf Port 80 läuft evtl. schon eine andere Web-Applikation, die evtl. sogar in PHP, Python oder Java realisiert wurde – was nun?

Eine Lösung für dieses Problem ist die Einrichtung eines “Reverse Proxies” im Apache Webserver. Dies hat den Vorteil, dass man die next.js Applikation mit einem VirtualHost verknüpfen kann der https unterstützt ohne dass man sich in der Next.js Appliation in irgendeiner Form darum kümmern muss. Ausserdem ist es total einfach 🙂

Angenommen man hat eine next.js Web-Applikation unter http://localhost:18080/ laufen und möchte diese nun im öffentlichen Internet über https://next.js.dieletztedomain.de/ erreichen können.Um dies zu erreichen sind mehrere Schritte nötig.

Schritt 1 ist die Registrierung der Domain und das Einrichten eines virtuellen Hosts im Apache. Mit dem Apache2 unter Ubuntu geht das wie folgt. Andere Systeme funktionieren aber eigentlich fast genauso.

Da ich keine extra Domain registrieren möchte nehme ich die Subdomain mit dem Namen next.js.dieletztedomain.de. Da mein Nameserver so konfiguriert ist, dass alle Subdomains der Domain dieletztedomain.de auf die gleiche IP-Adresse zeigen (Wildcard DNS record), kann ich direkt mit dem VirtualHost Eintrag im Apache starten.

Die Apache Konfigurationsdatei sieht erst mal wie folgt aus:

 <Directory /var/www/next.js.dieletztedomain.de/http>
         Require all granted
     AllowOverride all
 </Directory>
 <Directory /var/www/next.js.dieletztedomain.de/https>
         Require all granted
     AllowOverride all
 </Directory>
 <VirtualHost *:80>
     ServerName next.js.dieletztedomain.de
     ServerAlias www.next.js.dieletztedomain.de
     ServerAdmin webmaster@localhost DocumentRoot /var/www/next.js.dieletztedomain.de/http 
    ErrorLog ${APACHE_LOG_DIR}/next.js.dieletztedomain.de.error.log 
    CustomLog ${APACHE_LOG_DIR}/next.js.dieletztedomain.de.access.log combined
 </VirtualHost>

Damit existiert erst mal mal eine Konfiguration für den VirtualHost, die mit den beiden Befehlen “a2ensite next.js.dieletztedomain.de.conf” und “systemctl reload apache2” aktiviert wird. Ruft man nun http://next.js.dieletztedomain.de/ im Browser auf erhält man einen Fehler (404 Not Found) weil natürlich noch nichts da ist. Aber mit der Konfiguration kann nun das entsprechende Let’s Encrypt Zertifikat erstellt werden.

sudo certbot --apache certonly  -d next.js.dieletztedomain.de -d www.next.js.dieletztedomain.de

Nach ein paar Sekunden wird der Aufruf des certbot mit der folgenden Ausgabe beendet.

 Saving debug log to /.../letsencrypt.log
 Plugins selected: Authenticator apache, Installer apache
 Obtaining a new certificate
 Performing the following challenges:
 http-01 challenge for next.js.dieletztedomain.de
 http-01 challenge for www.next.js.dieletztedomain.de
 Waiting for verification…
 Cleaning up challenges
 IMPORTANT NOTES:
 Congratulations! Your certificate and chain have been saved at:
 /.../next.js.dieletztedomain.de/fullchain.pem
 Your key file has been saved at:
 /.../next.js.dieletztedomain.de/privkey.pem
 Your cert will expire on 2020-03-15. To obtain a new or tweaked
 version of this certificate in the future, simply run certbot
 again. To non-interactively renew all of your certificates, run
 "certbot renew"
 If you like Certbot, please consider supporting our work by:
 Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 Donating to EFF:                    https://eff.org/donate-le 

Damit ist das Zertifikat erstellt und kann in die VirtualHost Konfiguration des Apache eingetragen werden. Zusätzlich kann nun auch schon der ReverseProxy in der Apache Konfiguration eingetragen werden über die dann die next.js Applikation aufgerufen werden kann.

<VirtualHost *:443>
    ServerName next.js.dieletztedomain.de
    ServerAlias www.next.js.dieletztedomain.de
    ServerAdmin webmaster@localhost 
    DocumentRoot /var/www/next.js.dieletztedomain.de/https 
    ErrorLog ${APACHE_LOG_DIR}/next.js.dieletztedomain.de.error.log    
    CustomLog ${APACHE_LOG_DIR}/next.js.dieletztedomain.de.access.log combined 
    SSLEngine on 
    ProxyRequests Off 
    ProxyPreserveHost On 
    ProxyPass / http://localhost:18080/ 
    ProxyPassReverse / http://localhost:18080/ 
    SSLCertificateFile /etc/letsencrypt/live/next.js.dieletztedomain.de/cert.pem 
    SSLCertificateKeyFile /etc/letsencrypt/live/next.js.dieletztedomain.de/privkey.pem 
    SSLCertificateChainFile /etc/letsencrypt/live/next.js.dieletztedomain.de/chain.pem
 </VirtualHost>

Nun noch den next.js Service auf Port 18080 starten und schon hat mein eine Next.js Applikation unter einem virtuellen Host incl. https mit Let’s Encrypt Zertifikat laufen.

Zum Schluss schütze ich den VirtualHost noch per htaccess vor unberechtigtem Zugriff. Dazu erstelle ich mir eine “.htpasswd” Datei mit dem htpasswd Tool und trage folgende Zeilen in die VirtualHost Konfiguration hinter den ProxyPassReverse Eintrag ein.

    <Location>
        AuthType Basic
        AuthName "Wrapper auth"
        AuthBasicProvider file
        AuthUserFile "/..../.htpasswd"
        Require valid-user
    </Location>

Die wichtigsten Grundvoraussetzungen sind geschaffen und ich nun mit dem Programmieren eine next.js Applikation beginnen anfangen 🙂


Meine Apps im Google Play-Strore
Jetzt bei Google Play