Add custom error pages and Apache configuration#47
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds prerendered 404 and 410 error pages with navigation suggestions, and implements Apache
.htaccesstemplating to support dynamic base URLs for production and preview deployments.Changes
app/pages/404.vueandapp/pages/410.vuewith consistent styling, SEO metadata, and navigation links to home, labs, and speaking sectionspublic/.htaccesstopublic/.htaccess.tplwith:__BASE__for runtime base URL substitutionErrorDocumentdirectives pointing to prerendered error pages/tags,/en,/legal)nitro:inithook innuxt.config.tsto process the.htaccess.tpltemplate at build time, replacing__BASE__with the configured base URL and writing the result to.output/public/.htaccessnuxt.config.tsto:/404and/410in prerender routesImplementation details
The
.htaccess.tpltemplating 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