From cd38afc013a296518bc6ac55281f06546379e680 Mon Sep 17 00:00:00 2001 From: Santhosh Janardhanan Date: Sun, 19 Apr 2026 17:10:33 -0400 Subject: [PATCH] fix improve custom nginx error pages --- Dockerfile | 1 + deploy/nginx.conf | 19 +++-- site/src/pages/404.astro | 51 ++++++++++++++ site/src/pages/500.astro | 51 ++++++++++++++ site/src/styles/global.css | 141 +++++++++++++++++++++++++++++++++++++ 5 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 site/src/pages/404.astro create mode 100644 site/src/pages/500.astro diff --git a/Dockerfile b/Dockerfile index fc651c2..179ec1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ COPY site/ ./ # Content is fetched before build (typically in CI) and committed into the build context at # `site/content/cache/content.json`. If env vars aren't configured, the fetch step gracefully # skips sources and/or uses last-known-good cache. + RUN npm run build FROM nginx:1.27-alpine diff --git a/deploy/nginx.conf b/deploy/nginx.conf index 5c5f8e5..6532ed4 100644 --- a/deploy/nginx.conf +++ b/deploy/nginx.conf @@ -12,6 +12,10 @@ server { root /usr/share/nginx/html; index index.html; + # Custom error pages + error_page 404 /404.html; + error_page 500 502 503 504 /500.html; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.instagram.com https://*.instagram.com https://cloud.umami.is https://*.umami.is https://wa.santhoshj.com; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' https: data: blob:; media-src 'self' https://anchor.fm https://*.anchor.fm https://d3ctxlq1ktw2nl.cloudfront.net; connect-src 'self' https://cloud.umami.is https://*.umami.is https://wa.santhoshj.com; frame-src https://www.youtube.com https://open.spotify.com https://www.instagram.com https://*.instagram.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; @@ -24,15 +28,22 @@ server { try_files $uri =404; } - location ~* \.(?:avif|bmp|gif|ico|jpe?g|png|svg|webp)$ { + location ~* \.(?:avif|bmp|gif|ico|jpe?g|png|svg|webp|)$ { expires 180d; add_header Cache-Control "public, max-age=15552000" always; try_files $uri =404; } + # Ensure error pages are served without redirect + location = /404.html { + internal; + } + + location = /500.html { + internal; + } + location / { - # Serve directory index pages without requiring a trailing slash. - # This fixes /videos (and similar) resolving to /videos/index.html. try_files $uri $uri/index.html $uri/ =404; } -} +} \ No newline at end of file diff --git a/site/src/pages/404.astro b/site/src/pages/404.astro new file mode 100644 index 0000000..18749da --- /dev/null +++ b/site/src/pages/404.astro @@ -0,0 +1,51 @@ +--- +export const prerender = true; + +import BaseLayout from "../layouts/BaseLayout.astro"; +import CtaLink from "../components/CtaLink.astro"; +import { LINKS } from "../lib/links"; +--- + + +
+
404
+

Page not found

+

+ The page you're looking for doesn't exist or has been moved. +
+ Let's get you back on track. +

+ +
+
+

Popular destinations

+ +
+
+
diff --git a/site/src/pages/500.astro b/site/src/pages/500.astro new file mode 100644 index 0000000..22f35fd --- /dev/null +++ b/site/src/pages/500.astro @@ -0,0 +1,51 @@ +--- +export const prerender = true; + +import BaseLayout from "../layouts/BaseLayout.astro"; +import CtaLink from "../components/CtaLink.astro"; +import { LINKS } from "../lib/links"; +--- + + +
+
500
+

Something went wrong

+

+ We're experiencing a server issue. +
+ Please try again in a moment or explore other content. +

+ +
+
+

Popular destinations

+ +
+
+
diff --git a/site/src/styles/global.css b/site/src/styles/global.css index 4050e59..a8cd10d 100644 --- a/site/src/styles/global.css +++ b/site/src/styles/global.css @@ -1341,3 +1341,144 @@ button.card { padding: 12px 20px 20px; } } + +/* Error Pages (404, 500) */ +.error-hero { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 60vh; + padding: 40px 20px; + text-align: center; +} + +.error-code { + font-size: clamp(120px, 20vw, 200px); + font-weight: 800; + line-height: 1; + letter-spacing: -0.05em; + color: transparent; + background: linear-gradient(135deg, var(--accent), var(--accent2)); + -webkit-background-clip: text; + background-clip: text; + opacity: 0.9; + margin-bottom: 8px; +} + +.error-title { + font-size: clamp(28px, 5vw, 40px); + font-weight: 700; + letter-spacing: -0.02em; + margin: 0 0 16px; + color: var(--fg); +} + +.error-message { + font-size: clamp(16px, 2.5vw, 18px); + line-height: 1.6; + color: var(--muted); + max-width: 480px; + margin: 0 auto 32px; +} + +.error-actions { + display: flex; + gap: 12px; + flex-wrap: wrap; + justify-content: center; + margin-bottom: 48px; +} + +.error-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 14px 28px; + border-radius: 14px; + font-weight: 700; + font-size: 15px; + letter-spacing: -0.01em; + text-decoration: none; + transition: + background 120ms ease, + transform 120ms ease, + box-shadow 120ms ease; +} + +.error-btn-primary { + background: linear-gradient(135deg, rgba(94, 228, 255, 0.24), rgba(255, 205, 74, 0.16)); + border: 1px solid rgba(94, 228, 255, 0.55); + color: var(--fg); + box-shadow: + 0 0 0 3px rgba(94, 228, 255, 0.14), + 0 10px 30px rgba(0, 0, 0, 0.25); +} + +.error-btn-primary:hover { + transform: translateY(-2px); + box-shadow: + 0 0 0 4px rgba(94, 228, 255, 0.2), + 0 14px 40px rgba(0, 0, 0, 0.3); +} + +.error-btn-secondary { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03)); + border: 1px solid var(--stroke); + color: var(--fg); +} + +.error-btn-secondary:hover { + background: rgba(255, 255, 255, 0.12); + transform: translateY(-2px); +} + +.error-divider { + width: 80px; + height: 1px; + background: linear-gradient(90deg, transparent, var(--stroke-strong), transparent); + margin: 0 auto 40px; +} + +.error-suggestions { + width: 100%; + max-width: 600px; +} + +.error-suggestions-title { + font-size: 14px; + font-weight: 600; + letter-spacing: 0.02em; + text-transform: uppercase; + color: var(--muted); + margin: 0 0 20px; +} + +.error-links { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 12px; +} + +@media (max-width: 480px) { + .error-actions { + flex-direction: column; + width: 100%; + max-width: 280px; + } + + .error-btn { + width: 100%; + } + + .error-links { + flex-direction: column; + align-items: center; + } + + .error-links .cta { + width: 100%; + max-width: 200px; + } +}