diff --git a/apps/blog/app/layout.tsx b/apps/blog/app/layout.tsx
index 66710cba7cc2da3a84ecfd942de356f6547fed24..9575e635ace7b695380dfd300e2db9ff8d6dce7a 100644
--- a/apps/blog/app/layout.tsx
+++ b/apps/blog/app/layout.tsx
@@ -2,7 +2,7 @@ import '@hive/tailwindcss-config/globals.css';
import { ReactNode } from 'react';
import Script from 'next/script';
import { Metadata } from 'next';
-import { cookies, headers } from 'next/headers';
+import { cookies } from 'next/headers';
import MainBar from '../features/layouts/site-header/main-bar';
import ClientEffects from '../features/layouts/site-header/client-effects';
import { Providers } from '../features/layouts/providers';
@@ -11,15 +11,6 @@ import VisitLoggerClient from '../lib/visit-logger-client';
// Get basePath from build-time environment
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
-/**
- * Get the CSP nonce from the request headers.
- * The nonce is generated in middleware and passed via x-nonce header.
- */
-function getNonce(): string {
- const headersList = headers();
- return headersList.get('x-nonce') || '';
-}
-
const SITE_DESC =
'Communities without borders. A social network owned and operated by its users, powered by Hive.';
@@ -57,9 +48,6 @@ export default async function RootLayout({ children }: { children: ReactNode })
const locale = cookieStore.get('NEXT_LOCALE')?.value || 'en';
const isRTL = locale === 'ar';
- // Get nonce for CSP-compliant script loading
- const nonce = getNonce();
-
return (
@@ -72,7 +60,7 @@ export default async function RootLayout({ children }: { children: ReactNode })
>
-
+
diff --git a/apps/blog/middleware.ts b/apps/blog/middleware.ts
index aaaf9f2390abf65b41dd97fdbd49f31d2a6d8214..1aad2ad47fcd3a5565e098275fb7a7c0b8caf0ab 100644
--- a/apps/blog/middleware.ts
+++ b/apps/blog/middleware.ts
@@ -1,71 +1,23 @@
import { type NextRequest, NextResponse } from 'next/server';
import { setLoginChallengeCookies } from '@hive/smart-signer/lib/middleware-challenge-cookies';
-/**
- * Generate a cryptographically secure nonce for CSP.
- * In production, crypto.randomUUID() is available in Edge runtime.
- */
-function generateNonce(): string {
- // Use crypto.randomUUID() which is available in modern Edge runtime
- // Convert to base64 for CSP compatibility
- const uuid = crypto.randomUUID();
- // Use btoa-safe encoding (remove hyphens, then encode)
- return Buffer.from(uuid.replace(/-/g, ''), 'hex').toString('base64');
-}
-
-/**
- * Build CSP header value with nonce for script and style execution.
- * This provides strong XSS protection while allowing legitimate inline scripts.
- */
-function buildCspHeader(nonce: string): string {
- return [
- // Default fallback for unspecified resource types
- "default-src 'self'",
- // Scripts: only allow same-origin and nonced scripts
- // 'strict-dynamic' allows scripts loaded by nonced scripts
- `script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'wasm-unsafe-eval' https://platform.twitter.com`,
- // Styles: nonce-based (fallback to unsafe-inline for older browsers)
- `style-src 'self' 'nonce-${nonce}' 'unsafe-inline'`,
- // Images: self + any HTTPS + data URIs + blob
- "img-src 'self' https: data: blob:",
- // Fonts: self + data URIs
- "font-src 'self' data:",
- // API connections: whitelist of trusted Hive API nodes
- // Only nodes running proper haf_api_node software are allowed
- "connect-src 'self' https://api.hive.blog https://api.syncad.com https://api.openhive.network https://images.hive.blog",
- // Embedded content whitelist
- "frame-src https://platform.twitter.com https://www.instagram.com https://player.vimeo.com https://www.youtube.com https://w.soundcloud.com https://player.twitch.tv https://open.spotify.com https://3speak.tv https://3speak.online https://3speak.co https://emb.d.tube https://odysee.com https://openhive.chat",
- // Web Workers
- "worker-src 'self' blob:",
- // Clickjacking protection
- "frame-ancestors 'self'",
- // Restrict base URI
- "base-uri 'self'",
- // Restrict form submissions
- "form-action 'self'",
- // Report violations
- "report-uri /api/csp-report"
- ].join('; ');
-}
+// NOTE: Nonce-based CSP is disabled because Next.js 14 doesn't fully support it.
+// Next.js internal scripts (__NEXT_DATA__, hydration) don't receive nonces automatically,
+// and inline style attributes (style-src-attr) don't support nonces at all.
+// The static CSP in next.config.js provides protection without causing violations.
+// See GitLab issue #796 for tracking nonce CSP support in future Next.js versions.
export async function middleware(request: NextRequest): Promise {
const { pathname } = request.nextUrl;
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || '';
- // Generate nonce for this request
- const nonce = generateNonce();
-
const res = NextResponse.next();
// Set login challenge cookies (needed for console logging process)
setLoginChallengeCookies(request, res);
- // Set CSP header with nonce
- // Using Report-Only initially for safety - switch to Content-Security-Policy when ready
- res.headers.set('Content-Security-Policy-Report-Only', buildCspHeader(nonce));
-
- // Pass nonce to app via header (to be read in layout.tsx)
- res.headers.set('x-nonce', nonce);
+ // CSP is now set via next.config.js headers() - no middleware override needed
+ // The static CSP uses 'unsafe-inline' which is compatible with Next.js
// In blog, redirect root path to /trending
if (pathname === '/' || pathname === `${basePath}` || pathname === `${basePath}/`) {