Skip to content

Add custom error pages and Apache configuration#47

Merged
dinooo13 merged 6 commits into
mainfrom
claude/plan-404-issue-jKWct
May 16, 2026
Merged

Add custom error pages and Apache configuration#47
dinooo13 merged 6 commits into
mainfrom
claude/plan-404-issue-jKWct

Conversation

@dinooo13
Copy link
Copy Markdown
Owner

Summary

Adds prerendered 404 and 410 error pages with navigation suggestions, and implements Apache .htaccess templating to support dynamic base URLs for production and preview deployments.

Changes

  • New error pages: Created app/pages/404.vue and app/pages/410.vue with consistent styling, SEO metadata, and navigation links to home, labs, and speaking sections
  • Apache configuration templating: Renamed public/.htaccess to public/.htaccess.tpl with:
    • Template variable __BASE__ for runtime base URL substitution
    • Custom ErrorDocument directives pointing to prerendered error pages
    • Rewrite rules returning HTTP 410 Gone for permanently removed URL patterns (/tags, /en, /legal)
  • Nitro hook: Added nitro:init hook in nuxt.config.ts to process the .htaccess.tpl template at build time, replacing __BASE__ with the configured base URL and writing the result to .output/public/.htaccess
  • Build configuration: Updated nuxt.config.ts to:
    • Include /404 and /410 in prerender routes
    • Exclude error pages from sitemap generation

Implementation details

The .htaccess.tpl templating approach allows the same build artifact to work correctly for both production (/) and preview (/pr-<n>/) deployments by resolving the base URL at generate time rather than runtime. The hook runs after the Nitro build completes and cleans up the leaked template file from the output directory.

https://claude.ai/code/session_01J2eCfV7Lw9VWh6njMeWVVe

Adds two prerendered error pages (`/404` and `/410`) wrapped by the
default layout so missing and removed URLs get the site's actual
chrome and copy instead of Apache's defaults. `/tags*`, `/en*`, and
`/legal` are rewritten to 410 Gone so Google de-indexes them faster
than 404.

`.htaccess` is now templated from `public/.htaccess.tpl` at generate
time so the `ErrorDocument` and `RewriteBase` paths resolve correctly
for both production (/) and preview (/pr-<n>/) builds. Closes #46.
upload-artifact strips dotfiles by default, so .htaccess never reached
the deploy step. That left both the security headers (added in #20)
and the new 404/410 ErrorDocument rules missing in production and
preview — Apache fell back to the host's default error page.
ColorModeButton builds icon names dynamically (`i-lucide-${...}`), so
the build-time scanner can't see them and they fell back to
`api.iconify.design` at runtime. CSP `connect-src 'self'` (now actually
applied since the .htaccess deploys) blocked the request.

List sun/moon explicitly in the client bundle and set
`fallbackToApi: "server-only"` so the runtime never reaches out to the
public Iconify CDN.
Client-side navigation through Nuxt Content v3 instantiates a
WebAssembly-compiled sqlite for in-browser queries. The strict
`script-src 'self' 'unsafe-inline'` blocked `WebAssembly.instantiate`,
breaking navigation away from the 404 page.

`'wasm-unsafe-eval'` is the dedicated CSP source for exactly this
case — it permits WebAssembly compilation without enabling general
JavaScript `eval()`.
The scanner is off by default, so only the two explicitly-listed icons
were bundled. After navigating from /404 to home, every dynamically
referenced icon (those bound via `:icon="page.icon"` from YAML, plus
the chevrons and arrows used inside @nuxt/ui components) tried to
fetch from `api.iconify.design` and got blocked by CSP.

Turn on `clientBundle.scan` so the scanner picks up icon strings in
.vue and .yml files, and explicitly list the @nuxt/ui internals
(chevron-down, arrow-right, arrow-up-right) since those live in
node_modules and the scanner skips them. Result: 21 icons inlined,
6 KB uncompressed.
The scanner's default globs cover .vue / .yml / .md etc. but not .ts,
so icon references in app/utils/* and app.config.ts (labStatusIconMap
'sparkles' / 'pause', toast icons, footer links, nav links) were
missed and fell back to api.iconify.design at runtime.

Extend globInclude to add .ts and .mts. Bundle now resolves 27 icons
inline (8 KB) — every literal icon reference in the project.
@dinooo13 dinooo13 merged commit 28df995 into main May 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants