Add Content Security Policy headers (#805)

* Basic CSP with unsafe-inline, unsafe-eval

* Allow 'self' for img-src and connect-src

Apparently, there is a bug for Chrome on iOS if connect-src does not allow 'self'.

See known issues at https://caniuse.com/contentsecuritypolicy

* Use nonces for strict CSP

* More CSP comments

* Add frame-ancestors directive

* Add more useful headers

* Add HSTS header

* Allow youtube and twitter embeds

For some reason, www.youtube.com is enough. It also works for youtube.com and youtube-nocookie.com.

For twitter embeds from twitter.com or x.com, platform.twitter.com is enough.

* Allow CDN and media domain in CSP

* Only allow unsafe-eval in dev build

* Ignore _next/webpack-hmr in middleware
This commit is contained in:
ekzyis 2024-02-13 20:10:06 +01:00 committed by GitHub
parent a4e84e7a2e
commit fc18a917e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 188 additions and 102 deletions

View File

@ -1,8 +1,8 @@
import { NextResponse } from 'next/server'
export function middleware (request) {
const regex = /(\/.*)?\/r\/([\w_]+)/
const m = regex.exec(request.nextUrl.pathname)
const referrerRegex = /(\/.*)?\/r\/([\w_]+)/
function referrerMiddleware (request) {
const m = referrerRegex.exec(request.nextUrl.pathname)
const url = new URL(m[1] || '/', request.url)
url.search = request.nextUrl.search
@ -13,6 +13,64 @@ export function middleware (request) {
return resp
}
export const config = {
matcher: ['/(.*/|)r/([\\w_]+)([?#]?.*)']
export function middleware (request) {
let resp = NextResponse.next()
if (referrerRegex.test(request.nextUrl.pathname)) {
resp = referrerMiddleware(request)
}
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = [
// if something is not explicitly allowed, we don't allow it.
"default-src 'none'",
"font-src 'self' a.stacker.news",
// we want to load images from everywhere but we can limit to HTTPS at least
"img-src 'self' a.stacker.news m.stacker.news https: data:",
// Using nonces and strict-dynamic deploys a strict CSP.
// see https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#strict-policy.
// Old browsers will ignore nonce and strict-dynamic
// and fallback to host matching, unsafe-inline and unsafe-eval (no protection against XSS)
process.env.NODE_ENV === 'production'
? `script-src 'self' 'unsafe-inline' 'nonce-${nonce}' 'strict-dynamic' https:`
// unsafe-eval is required during development due to react-refresh.js
// see https://github.com/vercel/next.js/issues/14221
: `script-src 'self' 'unsafe-inline' 'unsafe-eval' 'nonce-${nonce}' 'strict-dynamic' https:`,
// unsafe-inline for styles is not ideal but okay if script-src is using nonces
"style-src 'self' a.stacker.news 'unsafe-inline'",
"manifest-src 'self'",
'frame-src www.youtube.com platform.twitter.com',
"connect-src 'self' https: wss:",
// disable dangerous plugins like Flash
"object-src 'none'",
// blocks injection of <base> tags
"base-uri 'none'",
// tell user agents to replace HTTP with HTTPS
'upgrade-insecure-requests',
// prevents any domain from framing the content (defense against clickjacking attacks)
"frame-ancestors 'none'"
].join('; ')
resp.headers.set('Content-Security-Policy', cspHeader)
// for browsers that don't support CSP
resp.headers.set('X-Frame-Options', 'DENY')
// more useful headers
resp.headers.set('X-Content-Type-Options', 'nosniff')
resp.headers.set('Referrer-Policy', 'origin-when-cross-origin')
resp.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
return resp
}
export const config = {
matcher: [
// NextJS recommends to not add the CSP header to prefetches and static assets
// See https://nextjs.org/docs/app/building-your-application/configuring/content-security-policy
{
source: '/((?!api|_next/static|_next/image|_next/webpack-hmr|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' }
]
}
]
}

View File

@ -1,17 +1,42 @@
import { Html, Head, Main, NextScript } from 'next/document'
import Document, { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
export default function Document () {
return (
<Html lang='en'>
<Head>
<link rel='manifest' href='/api/site.webmanifest' />
<link rel='preload' href={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.woff2`} as='font' type='font/woff2' crossOrigin='' />
<link rel='preload' href={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.woff`} as='font' type='font/woff' crossOrigin='' />
<link rel='preload' href={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.ttf`} as='font' type='font/ttf' crossOrigin='' />
<style
dangerouslySetInnerHTML={{
__html:
class MyDocument extends Document {
// https://nextjs.org/docs/pages/building-your-application/routing/custom-document#customizing-renderpage
static async getInitialProps (ctx) {
const originalRenderPage = ctx.renderPage
// Run the React rendering logic synchronously
ctx.renderPage = () =>
originalRenderPage({
// Useful for wrapping the whole react tree
enhanceApp: (App) => App,
// Useful for wrapping in a per-page basis
enhanceComponent: (Component) => Component
})
// response is not available on offline page for example
const csp = ctx.res?.getHeaders()['content-security-policy']
const nonce = csp ? /nonce-([a-zA-Z0-9]{48})/.exec(csp)?.[1] : undefined
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps, nonce }
}
render () {
const { nonce } = this.props
return (
<Html lang='en'>
<Head nonce={nonce}>
<link rel='manifest' href='/api/site.webmanifest' />
<link rel='preload' href={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.woff2`} as='font' type='font/woff2' crossOrigin='' />
<link rel='preload' href={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.woff`} as='font' type='font/woff' crossOrigin='' />
<link rel='preload' href={`${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.ttf`} as='font' type='font/ttf' crossOrigin='' />
<style
nonce={nonce}
dangerouslySetInnerHTML={{
__html:
` @font-face {
font-family: 'lightning';
src: url(${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.ttf) format('truetype'),
@ -19,13 +44,13 @@ export default function Document () {
url(${process.env.NEXT_PUBLIC_ASSET_PREFIX}/Lightningvolt-xoqm.woff2) format('woff2');
font-display: swap;
}`
}}
/>
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta name='theme-color' content='#121214' />
<link rel='apple-touch-icon' href='/icons/icon_x192.png' />
<Script id='dark-mode-js' strategy='beforeInteractive'>
{`const handleThemeChange = (dark) => {
}}
/>
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta name='theme-color' content='#121214' />
<link rel='apple-touch-icon' href='/icons/icon_x192.png' />
<Script id='dark-mode-js' strategy='beforeInteractive'>
{`const handleThemeChange = (dark) => {
const root = window.document.documentElement
root.setAttribute('data-bs-theme', dark ? 'dark' : 'light')
}
@ -58,82 +83,85 @@ export default function Document () {
handleThemeChange(dark)
})()
}`}
</Script>
{/* light-theme splash screen links */}
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14_Pro_Max_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14_Pro_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/iPhone_11__iPhone_XR_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/12.9__iPad_Pro_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/10.9__iPad_Air_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/10.5__iPad_Air_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/10.2__iPad_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/8.3__iPad_Mini_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14_Pro_Max_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14_Pro_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/iPhone_11__iPhone_XR_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/12.9__iPad_Pro_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/10.9__iPad_Air_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/10.5__iPad_Air_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/10.2__iPad_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/8.3__iPad_Mini_portrait.png' />
{/* dark-theme splash screen links */}
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_Max_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_11__iPhone_XR_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/12.9__iPad_Pro_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/10.9__iPad_Air_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/10.5__iPad_Air_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/10.2__iPad_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/8.3__iPad_Mini_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_Max_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_11__iPhone_XR_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/12.9__iPad_Pro_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/10.9__iPad_Air_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/10.5__iPad_Air_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/10.2__iPad_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/8.3__iPad_Mini_portrait_dark.png' />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
</Script>
{/* light-theme splash screen links */}
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14_Pro_Max_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14_Pro_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/iPhone_11__iPhone_XR_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/12.9__iPad_Pro_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/10.9__iPad_Air_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/10.5__iPad_Air_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/10.2__iPad_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape)' href='/splash/8.3__iPad_Mini_landscape.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14_Pro_Max_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14_Pro_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/iPhone_11__iPhone_XR_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/12.9__iPad_Pro_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/10.9__iPad_Air_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/10.5__iPad_Air_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/10.2__iPad_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)' href='/splash/8.3__iPad_Mini_portrait.png' />
{/* dark-theme splash screen links */}
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_Max_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_11__iPhone_XR_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/12.9__iPad_Pro_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/10.9__iPad_Air_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/10.5__iPad_Air_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/10.2__iPad_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: landscape) and (prefers-color-scheme: dark)' href='/splash/8.3__iPad_Mini_landscape_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_Max_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Pro_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_11_Pro_Max__iPhone_XS_Max_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_11__iPhone_XR_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/4__iPhone_SE__iPod_touch_5th_generation_and_later_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/12.9__iPad_Pro_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/11__iPad_Pro__10.5__iPad_Pro_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 820px) and (device-height: 1180px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/10.9__iPad_Air_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/10.5__iPad_Air_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 810px) and (device-height: 1080px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/10.2__iPad_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/9.7__iPad_Pro__7.9__iPad_mini__9.7__iPad_Air__9.7__iPad_portrait_dark.png' />
<link rel='apple-touch-startup-image' media='screen and (device-width: 744px) and (device-height: 1133px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait) and (prefers-color-scheme: dark)' href='/splash/8.3__iPad_Mini_portrait_dark.png' />
</Head>
<body>
<Main />
<NextScript nonce={nonce} />
</body>
</Html>
)
}
}
export default MyDocument