ガイド 第40

ビルド設定 & セキュリティヘッダ

createNextConfig・CSP・distDir 分離

@event/core

next.config は各イベントで手書きしない。@event/corecreateNextConfig()全イベント共通の既定を供給する。spikes はヘッダ皆無、ivs は CSP 無し、xdeal だけが CSP/HSTS を持つ、 という3実装のばらつきをこの factory で一本化した(@harvested-from xdeal-lp / ivs-cms next.config.ts)。

starter の実配線

import { createNextConfig } from "@event/core";

const nextConfig = createNextConfig({
  transpilePackages: [
    "@event/schema", "@event/fours-sdk", "@event/core", "@event/cms",
    "@event/session-card", "@event/shell", /* … @event/* を列挙 … */
  ],
  security: { csp: { fourS: true, vercelBlob: true } },
});

export default nextConfig;

security を省くと堅い既定 CSP がそのまま付く。security: false で全ヘッダを外せる。

createNextConfig が黙って供給する既定

既定中身根治した事故
distDir 分離NEXT_DIST_DIR / BUILD_LOCAL=1.next-localdev と build が同じ .next を共有して dev が壊れる
dev memory cachewebpack devcache = { type: "memory" }.next/cache 破損 → 全 route 500
removeConsole本番で console.log/info/debug 除去(error/warn は残す)本番ログ漏れ
reactStrictModetrue 固定
security headersbuildSecurityHeaders()/(.*) に適用ヘッダ未設定アプリ

CreateNextConfigOptions の残りの knob: tracingExcludes(onnxruntime 等の tracing 除外・Vercel 250MB 対策)、 imageHostsimages.remotePatterns を https 前提で生成)、extend(最後に浅マージ・headers/webpack は上書き注意)。 BUILD_LOCAL=1 は「dev 稼働中にローカル build を打つ」ときの distDir を分けるスイッチで、dev の .next を壊さない。

buildSecurityHeaders — 常時付くヘッダ

securitySecurityHeadersOptions を渡す(csp / cspReportOnly(既定 true)/ frameOptions(既定 SAMEORIGIN))。 CSP 以外は無条件で付く:

ヘッダ
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preload
X-Content-Type-Optionsnosniff
Referrer-Policystrict-origin-when-cross-origin
Permissions-Policycamera=() microphone=() geolocation=() interest-cohort=() payment=() usb=() …(全無効)
Cross-Origin-Opener-Policysame-origin
Cross-Origin-Resource-Policysame-origin
X-Frame-OptionsSAMEORIGINframeOptions で DENY / false)

cspReportOnly(既定 true)だと enforce と同一の CSP を Content-Security-Policy-Report-Only にも二重出力する。 違反を観察しつつ強制もかける構えである。

buildCsp — フラグで connect-src が伸びる

CSP は self ベースの堅い構成が既定で、外部サービスを使うぶんだけフラグで開ける。既定 ON は fourS / vercelBlob / googleFonts、既定 OFF はアナリティクス系・unsafeEval / captureExport 等:

security: {
  csp: {
    fourS: true,        // connect-src に api.4s.link / static.4s.link / cognito-idp を許可(既定 true)
    vercelBlob: true,   // *.public.blob.vercel-storage.com(既定 true)
    vercelAnalytics: false, googleAnalytics: false, metaPixel: false,
    turnstile: false, sentry: false, googleFonts: true,
    unsafeEval: false,    // html2canvas 系 capture / dev でのみ true
    captureExport: false, // connect-src に data:/blob:(modern-screenshot の inline capture 用)
    extra: { "img-src": ["https://cdn.example.com"] }, // 同 key は空白結合で追記
  },
},
フラグ既定開く先(抜粋)
fourStrueconnect-src: api.4s.link api-staging2/-develop2.4s.link static.4s.link cognito-idp.ap-northeast-1.amazonaws.com
vercelBlobtrueconnect-src: https://*.public.blob.vercel-storage.com
googleFontstruestyle-src fonts.googleapis.com / font-src fonts.gstatic.com
turnstile sentry googleAnalytics metaPixel vercelAnalyticsfalse各サービスの script/connect/frame
unsafeEvalfalsescript-src に 'unsafe-eval'
captureExportfalseconnect-src に data: blob:

固定の骨格は毎回同じ: default-src 'self' / img-src 'self' data: blob: https: / object-src 'none' / frame-ancestors 'self' / form-action 'self' / base-uri 'self' / upgrade-insecure-requests。 script-src は 'self' 'unsafe-inline'(Next の inline スクリプト都合)を基本とし、フラグで足す。 csp に文字列を直接渡せば buildCsp を通さず生の CSP を使える。csp: false で CSP だけ外す(他ヘッダは残る)。