diff --git a/blog/2025-06-openapi-generator/README.md b/blog/2025-06-openapi-generator/README.md
index 6d67f961..e9330add 100644
--- a/blog/2025-06-openapi-generator/README.md
+++ b/blog/2025-06-openapi-generator/README.md
@@ -2,7 +2,7 @@
title: "Generating Angular API clients with OpenAPI Generator"
author: Johannes Hoppe
mail: johannes.hoppe@haushoppe-its.de
-bio: 'This article complements the HTTP chapter of our new Angular book (in German language) by Ferdinand Malcher, Danny Koppenhagen and Johannes Hoppe, where we cover everything you need to know about HTTP communication with Angular. After four successful editions, we have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book will be released in May 2026. More info at angular-buch.com'
+bio: 'This article complements the HTTP chapter of our new Angular book (in German language) by Ferdinand Malcher, Danny Koppenhagen and Johannes Hoppe, where we cover everything you need to know about HTTP communication with Angular. After four successful editions, we have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book is out now. More info at angular-buch.com'
bioHeading: About the Angular book
author2: Angular.Schule Team
mail2: team@angular.schule
diff --git a/blog/2026-02-agentic-coding/README.md b/blog/2026-02-agentic-coding/README.md
index 46017d42..b35e62b2 100644
--- a/blog/2026-02-agentic-coding/README.md
+++ b/blog/2026-02-agentic-coding/README.md
@@ -2,7 +2,7 @@
title: 'Agentic Coding: AI Support for Angular'
author: Angular-Buch Team
mail: team@angular-buch.com
-bio: 'This article is an excerpt from the new Angular book (in German language) by Ferdinand Malcher, Danny Koppenhagen and Johannes Hoppe. After four successful editions, we have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book will be released in May 2026. More info at angular-buch.com'
+bio: 'This article is an excerpt from the new Angular book (in German language) by Ferdinand Malcher, Danny Koppenhagen and Johannes Hoppe. After four successful editions, we have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book is out now. More info at angular-buch.com'
bioHeading: About the Angular book
author2: Angular.Schule Team
mail2: team@angular.schule
diff --git a/blog/2026-02-claude-code/README.md b/blog/2026-02-claude-code/README.md
index f00e0cc7..c5a9c28f 100644
--- a/blog/2026-02-claude-code/README.md
+++ b/blog/2026-02-claude-code/README.md
@@ -2,7 +2,7 @@
title: 'Claude Code: The AI Agent for Angular Developers (and Those Who Want to Become One)'
author: Johannes Hoppe
mail: johannes.hoppe@haushoppe-its.de
-bio: 'Johannes Hoppe is a trainer, consultant and developer specializing in Angular. He is a co-author of the Angular book (in German language), together with Ferdinand Malcher and Danny Koppenhagen. After four successful editions, they have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book will be released in May 2026. More info at angular-buch.com'
+bio: 'Johannes Hoppe is a trainer, consultant and developer specializing in Angular. He is a co-author of the Angular book (in German language), together with Ferdinand Malcher and Danny Koppenhagen. After four successful editions, they have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book is out now. More info at angular-buch.com'
bioHeading: About Johannes Hoppe
author2: Angular.Schule Team
mail2: team@angular.schule
diff --git a/blog/2026-03-mini-typescript-hero/README.md b/blog/2026-03-mini-typescript-hero/README.md
index 282d2b93..fe27cc44 100644
--- a/blog/2026-03-mini-typescript-hero/README.md
+++ b/blog/2026-03-mini-typescript-hero/README.md
@@ -2,7 +2,7 @@
title: 'TypeScript Hero is dead (is yet another VS Code extension gone forever?)'
author: Johannes Hoppe
mail: johannes.hoppe@haushoppe-its.de
-bio: 'Johannes Hoppe is a trainer, consultant and developer specializing in Angular. He is a co-author of the Angular book (in German language), together with Ferdinand Malcher and Danny Koppenhagen. After four successful editions, they have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book will be released in May 2026. More info at angular-buch.com'
+bio: 'Johannes Hoppe is a trainer, consultant and developer specializing in Angular. He is a co-author of the Angular book (in German language), together with Ferdinand Malcher and Danny Koppenhagen. After four successful editions, they have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book is out now. More info at angular-buch.com'
bioHeading: About Johannes Hoppe
author2: Angular.Schule Team
mail2: team@angular.schule
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/README.md b/blog/2026-09-static-angular-ssr-trailing-slash/README.md
new file mode 100644
index 00000000..d5faacc9
--- /dev/null
+++ b/blog/2026-09-static-angular-ssr-trailing-slash/README.md
@@ -0,0 +1,236 @@
+---
+title: "Static Angular SSR: Nice URLs and good SEO - you can't have both (but here's a fix!)"
+author: Johannes Hoppe
+mail: johannes.hoppe@haushoppe-its.de
+bio: 'Johannes Hoppe is a trainer, consultant and developer specializing in Angular. He is a co-author of the Angular book (in German language), together with Ferdinand Malcher and Danny Koppenhagen. After four successful editions, they have rewritten the book from scratch – with modern syntax, compact and covering many new topics. The new book is out now. More info at angular-buch.com'
+bioHeading: About Johannes Hoppe
+author2: Angular.Schule Team
+mail2: team@angular.schule
+bio2: 'Want to write clean, well-structured code with best practices in mind? Join Ferdinand Malcher and Johannes Hoppe in our workshops, where you learn Angular the practical way – including modern tools for more efficient development. More at angular.schule'
+bio2Heading: About our Angular workshops
+published: 2026-09-25
+keywords:
+ - Angular
+ - Angular 22
+ - SSR
+ - SSG
+ - Prerendering
+ - Static Site Generation
+ - outputMode static
+ - Trailing Slash
+ - Redirect
+ - SEO
+ - GitHub Pages
+ - Cloudflare Pages
+ - Astro build.format
+ - prerenderFormat
+ - TrailingSlashPathLocationStrategy
+language: en
+header: header.jpg
+---
+
+I love Angular.
+But when it comes to prerendered static sites, Angular is surprisingly far behind other major frameworks.
+Astro, Next.js, SvelteKit: they all let you choose how your pages are written to disk.
+Angular doesn't.
+
+And static sites are great: hosting is literally free, and a CDN serves your pages to the whole world and scales like a beast.
+That's why the websites of Angular.Schule and the Angular book are prerendered static sites.
+
+But here comes the catch!
+Prerender your app, deploy it to a static host (for example GitHub Pages, Cloudflare Pages or Firebase Hosting), and take a look at the network tab: every direct visit starts with a redirect to the same URL with a trailing slash, and a moment later the Angular router quietly removes the slash again.
+It looks great in the address bar, but technically, for search engine optimization, it's a nightmare.
+You can get rid of the redirect, but only by putting a trailing slash on every URL of your site.
+**Nice URLs or good SEO: with Angular's prerendering, you can't have both. In this article, I explain why, and how to get both today.**
+
+## Contents
+
+[[toc]]
+
+## Today: nice URLs or good SEO, you can't have both
+
+Angular's prerendering (SSG) writes every route into its own folder.
+Take the article you are reading right now: with Angular's default, the route `blog/2026-09-static-angular-ssr-trailing-slash` becomes `blog/2026-09-static-angular-ssr-trailing-slash/index.html`.
+For the rest of this article, let's call it `blog/my-article`.
+
+A static host like GitHub Pages or Cloudflare Pages sees a request for `/blog/my-article`, finds a folder with that name and redirects to `/blog/my-article/`.
+
+```bash
+$ curl -I https://example.com/blog/my-article
+HTTP/2 301
+location: https://example.com/blog/my-article/
+```
+
+The browser follows the redirect, gets the `index.html`, Angular starts, and the router normalizes the URL back to `/blog/my-article`.
+So you have to choose:
+
+- **Nice URLs, but redirects:** Your links use `/blog/my-article`. Every direct visit (a search engine, a bookmark, a link shared on social media) starts with a redirect to `/blog/my-article/`, and the Angular router then removes the trailing slash again. The URL in your links is never the URL that actually answers.
+- **No redirects, but trailing slashes everywhere:** Your links have to use `/blog/my-article/`. Pages answer directly, but every URL ends with a slash, and Angular needs an extra provider to keep it in the address bar: `{ provide: LocationStrategy, useClass: TrailingSlashPathLocationStrategy }` (see [`TrailingSlashPathLocationStrategy`](https://angular.dev/api/common/TrailingSlashPathLocationStrategy)).
+
+## Why does Angular do this?
+
+The file name is decided in exactly one place in the Angular CLI, the function `getRouteOutPath()` in `@angular/build`:
+
+```typescript
+function getRouteOutPath(route: string, baseHrefPathname: string): string {
+ const routeWithoutBaseHref = getRouteWithoutBaseHref(route, baseHrefPathname);
+
+ return stripLeadingSlash(posix.join(routeWithoutBaseHref, 'index.html'));
+}
+```
+
+Every route ends up as `/index.html`.
+There is no option to change that.
+This layout works on every web server, which makes it a good default.
+But many static hosts can do better: they serve `blog/my-article.html` under `/blog/my-article`, directly, without a redirect.
+Angular just never writes `blog/my-article.html`.
+
+## How do other frameworks handle this?
+
+Static site generators have offered this choice for a long time.
+Angular is the odd one out:
+
+| Framework | Option | `blog/my-article/index.html` | `blog/my-article.html` |
+|---|---|---|---|
+| **Astro** | `build.format` | `'directory'` (default) | `'file'` |
+| Next.js (static export) | `trailingSlash` | `true` | `false` (default) |
+| SvelteKit | `trailingSlash` | `'always'` | `'never'` (default) |
+| Nuxt 2 | `generate.subFolders` | `true` (default) | `false` |
+| Hugo | `uglyURLs` | `false` (default) | `true` |
+| **Angular** | – | always | – |
+
+Next.js and SvelteKit even write `blog/my-article.html` by default.
+And the [Astro documentation](https://docs.astro.build/en/reference/configuration-reference/#buildformat) recommends `build.format: 'file'` together with `trailingSlash: 'never'`, which is the combination Angular can't produce today.
+
+## With `@angular-schule/prerender-format`: nice URLs and good SEO, we deserve both!
+
+The fix is surprisingly small: write `blog/my-article.html` instead of `blog/my-article/index.html`.
+
+Until Angular can do this on its own, the builder [`@angular-schule/prerender-format`](https://www.npmjs.com/package/@angular-schule/prerender-format) can do the job for us.
+
+
+
+In fact, you are looking at it right now.
+The Angular.Schule website is built with it, and this article is served from `blog/2026-09-static-angular-ssr-trailing-slash.html`.
+Open the network tab and reload: no redirect, just a clean URL.
+
+- **Nice URLs:** `/blog/my-article`, without a trailing slash, in your links, in the address bar and in the server response alike.
+- **Good SEO:** every page answers directly. Search engines see no redirect, and the URL they crawl is the same one your links point to.
+
+And yes, `blog.html` and the folder `blog/` can live side by side: the host serves `/blog` from the file and `/blog/my-article` from the folder.
+
+### Installation
+
+```bash
+ng add @angular-schule/prerender-format
+ng build
+```
+
+`ng add` swaps the builder of your build target and sets the new option `prerenderFormat`:
+
+```json
+"build": {
+ "builder": "@angular-schule/prerender-format:application",
+ "options": {
+ "outputMode": "static",
+ "prerenderFormat": "file"
+ }
+}
+```
+
+- `"directory"` (default): `/blog/my-article` is written to `blog/my-article/index.html`, as today.
+- `"file"`: `/blog/my-article` is written to `blog/my-article.html`.
+
+I borrowed the terminology from Astro: `build.format` with `'directory'` and `'file'` became `prerenderFormat` with the same values.
+All other options stay as they are, the builder passes them on to `@angular/build:application`.
+
+A static build is required, by design.
+An `ssr` entry is fine as long as `outputMode` is `"static"`: Angular then uses it only during `ng build` to prerender the pages, and no server is deployed.
+If your build deploys a server (`"outputMode": "server"`, or `ssr` without `outputMode`), `ng add` and `ng build` stop with a clear error.
+With a server, there is nothing to fix anyway: the Node.js server generated by Angular serves static files with `redirect: false`, so `/blog/my-article` is never redirected to `/blog/my-article/` in the first place.
+
+### How it works
+
+The builder calls `buildApplication()` from `@angular/build` and replaces the internal function `prerenderPages()` at runtime, to rename the files before they are written.
+That's a hack.
+It relies on internal APIs, which is why I narrowed it down to Angular 22 only.
+But it's a well-tested hack: it writes the same files as my patch for the Angular CLI, and it runs on the websites of [Angular.Schule](https://angular.schule) and the [Angular book](https://angular-buch.com).
+I hope I don't have to maintain this package longer than necessary, because I have also prepared a sustainable fix.
+
+## The real fix: an option in the Angular CLI
+
+A built-in option for the Angular CLI is the clean solution.
+My colleague Ferdinand Malcher opened an issue for this back in 2024: [angular/angular-cli#29173](https://github.com/angular/angular-cli/issues/29173), *"SSG/Prerendering: Allow generating foo.html instead of foo/index.html"*.
+It didn't collect enough votes in the community voting process back then.
+That's a pity. Let's hope the problem gets more attention now.
+
+So I wrote the pull request myself: **[angular/angular-cli#34180](https://github.com/angular/angular-cli/pull/34180)** adds the same option `prerenderFormat` to Angular's own application builder:
+
+```json
+"build": {
+ "builder": "@angular/build:application",
+ "options": {
+ "outputMode": "static",
+ "prerenderFormat": "file"
+ }
+}
+```
+
+Once it lands, you only switch the builder name back to `@angular/build:application`.
+That's the plan.
+
+**If you want this in Angular, please give the [issue](https://github.com/angular/angular-cli/issues/29173) a 👍. Upvoting the [pull request](https://github.com/angular/angular-cli/pull/34180) could be helpful, too! :-)**
+The Angular team decides based on community votes.
+
+## Two things to watch out for
+
+### A route called `index`
+
+With `'file'`, a route `/index` would be written to `index.html` and overwrite your start page.
+A route `/blog/index` would become `blog/index.html`, which web servers serve for `/blog/`, not for `/blog/index`.
+Both builder and pull request stop with an error in this case.
+
+### Your local preview server
+
+If you preview the build locally with `express.static`, watch out: with `blog.html` next to the folder `blog/`, `express.static` finds the folder first and redirects `/blog` to `/blog/`.
+To fix that, add a small middleware in front of `express.static`: for a request like `/blog`, it checks whether `blog.html` exists and sends that file directly.
+Only if there is no such file does the request go on to `express.static`:
+
+```typescript
+app.use((req, res, next) => {
+ if (req.path.endsWith('/') || path.extname(req.path)) { return next(); }
+ const file = path.join(distFolder, decodeURIComponent(req.path) + '.html');
+ if (file.startsWith(distFolder) && fs.existsSync(file)) { return res.sendFile(file); }
+ next();
+});
+app.use(express.static(distFolder));
+```
+
+## Conclusion
+
+Angular's prerendering writes every route into a folder, and static hosts answer that with a redirect to a trailing slash.
+Today, you have to choose between nice URLs and a site without redirects.
+
+**We deserve both.**
+Write `.html` instead of `/index.html`, and static hosts serve your pages directly under clean URLs.
+Until Angular supports this natively, two commands are enough:
+
+```bash
+ng add @angular-schule/prerender-format
+ng build
+```
+
+And if you'd like to see this in Angular itself, please vote for the [issue](https://github.com/angular/angular-cli/issues/29173) and the [pull request](https://github.com/angular/angular-cli/pull/34180). 🚀
+
+
+
+**Further Reading:**
+
+- [angular/angular-cli#29173: SSG/Prerendering: Allow generating foo.html instead of foo/index.html](https://github.com/angular/angular-cli/issues/29173)
+- [angular/angular-cli#34180: feat(@angular/build): add `prerenderFormat` option](https://github.com/angular/angular-cli/pull/34180)
+- [`@angular-schule/prerender-format` on npm](https://www.npmjs.com/package/@angular-schule/prerender-format) and [on GitHub](https://github.com/angular-schule/prerender-format)
+- [Astro: `build.format`](https://docs.astro.build/en/reference/configuration-reference/#buildformat)
+- [Angular: Server-side and hybrid rendering](https://angular.dev/guide/ssr)
+- [Angular: `TrailingSlashPathLocationStrategy`](https://angular.dev/api/common/TrailingSlashPathLocationStrategy)
+
+Header image: photo from pxhere (CC0), Angular logo by Google (CC BY 4.0).
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/diagram-requests.svg b/blog/2026-09-static-angular-ssr-trailing-slash/diagram-requests.svg
new file mode 100644
index 00000000..53e1664d
--- /dev/null
+++ b/blog/2026-09-static-angular-ssr-trailing-slash/diagram-requests.svg
@@ -0,0 +1,43 @@
+
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/header-source/.gitignore b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/.gitignore
new file mode 100644
index 00000000..a2a9dcfa
--- /dev/null
+++ b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/.gitignore
@@ -0,0 +1,2 @@
+header.png
+node_modules
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/header-source/angular-logo-white.svg b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/angular-logo-white.svg
new file mode 100644
index 00000000..23754a62
--- /dev/null
+++ b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/angular-logo-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/header-source/header.html b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/header.html
new file mode 100644
index 00000000..61ae456b
--- /dev/null
+++ b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/header.html
@@ -0,0 +1,44 @@
+
+
+
+
+
Good SEO
+
Nice URLs
+
Why Not Both?
+
+
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/header-source/render.mjs b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/render.mjs
new file mode 100644
index 00000000..c324e053
--- /dev/null
+++ b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/render.mjs
@@ -0,0 +1,27 @@
+// Renders ../header.jpg (2000 x 1050) from header.html.
+//
+// Usage (needs the `playwright` package, e.g. from any repo that has it installed):
+// node render.mjs
+//
+// header.html composes:
+// - signs-orig.jpg: photo from pxhere, https://pxhere.com/en/photo/593969 (CC0 Public Domain, 5100 x 2898)
+// - angular-logo-white.svg: Angular shield from the Angular press kit (CC BY 4.0), fills set to white
+// - the sign labels, mapped onto the sign faces with a perspective transform (matrix3d).
+// The corners of each sign face are in the `data-quad` attributes (top-left, top-right,
+// bottom-right, bottom-left, in header pixels). Change the text there, not the corners.
+// - a black fade to the right (.fade) behind the logo.
+//
+// The screenshot is a PNG; convert it to JPEG afterwards, for example on macOS:
+// sips -s format jpeg -s formatOptions 85 header.png --out ../header.jpg
+
+import { chromium } from 'playwright';
+import { fileURLToPath } from 'node:url';
+
+const here = new URL('.', import.meta.url);
+const browser = await chromium.launch();
+const page = await browser.newPage({ viewport: { width: 2000, height: 1050 } });
+await page.goto(new URL('header.html', here).href);
+await page.waitForLoadState('networkidle');
+await page.locator('.c').screenshot({ path: fileURLToPath(new URL('header.png', here)) });
+await browser.close();
+console.log('header.png written, convert it to ../header.jpg');
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/header-source/signs-orig.jpg b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/signs-orig.jpg
new file mode 100644
index 00000000..d18f3677
Binary files /dev/null and b/blog/2026-09-static-angular-ssr-trailing-slash/header-source/signs-orig.jpg differ
diff --git a/blog/2026-09-static-angular-ssr-trailing-slash/header.jpg b/blog/2026-09-static-angular-ssr-trailing-slash/header.jpg
new file mode 100644
index 00000000..8e5e6b4a
Binary files /dev/null and b/blog/2026-09-static-angular-ssr-trailing-slash/header.jpg differ