docs/self-hosting/netlify.md
Netlify provides excellent static site hosting with a generous free tier.
| Setting | Value |
|---|---|
| Build command | npm run build |
| Publish directory | dist |
| Node version | 18+ |
Click "Deploy site" and wait for the build.
Create netlify.toml in your project root:
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "18"
# Required security headers for SharedArrayBuffer (used by LibreOffice WASM)
[[headers]]
for = "/*"
[headers.values]
Cross-Origin-Embedder-Policy = "require-corp"
Cross-Origin-Opener-Policy = "same-origin"
Cross-Origin-Resource-Policy = "cross-origin"
# Pre-compressed LibreOffice WASM binary - must be served with Content-Encoding
[[headers]]
for = "/libreoffice-wasm/soffice.wasm.gz"
[headers.values]
Content-Type = "application/wasm"
Content-Encoding = "gzip"
Cache-Control = "public, max-age=31536000, immutable"
# Pre-compressed LibreOffice WASM data - must be served with Content-Encoding
[[headers]]
for = "/libreoffice-wasm/soffice.data.gz"
[headers.values]
Content-Type = "application/octet-stream"
Content-Encoding = "gzip"
Cache-Control = "public, max-age=31536000, immutable"
# Cache other WASM files
[[headers]]
for = "*.wasm"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
Content-Type = "application/wasm"
# SPA routing
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
::: warning Important
The Cross-Origin-Embedder-Policy and Cross-Origin-Opener-Policy headers are required for Word/ODT/Excel/PowerPoint to PDF conversions. Without them, SharedArrayBuffer is unavailable and the LibreOffice WASM engine will fail to initialize.
The Content-Encoding: gzip headers on the .wasm.gz and .data.gz files tell the browser to decompress them automatically. Without these, the browser receives raw gzip bytes and WASM compilation fails.
:::
Set these in Site settings → Environment variables:
| Variable | Description |
|---|---|
SIMPLE_MODE | Set to true for minimal build |
VITE_BRAND_NAME | Custom brand name (replaces "BentoPDF") |
VITE_BRAND_LOGO | Logo path relative to public/ (e.g. images/my-logo.svg) |
VITE_FOOTER_TEXT | Custom footer/copyright text |
VITE_DEFAULT_LANGUAGE | Default UI language (e.g. fr, de, es) |
For large WASM files, consider enabling Netlify Large Media:
netlify lm:setup
git lfs track "*.wasm"
If document conversions hang at 55%, open DevTools Console and check:
console.log(window.crossOriginIsolated); // should be true
console.log(typeof SharedArrayBuffer); // should be "function"
If crossOriginIsolated is false, the COEP/COOP headers from your netlify.toml are not being applied. Make sure the file is in your project root and redeploy.
If you see expected magic word 00 61 73 6d, found 1f 8b 08 08 in the console, the .wasm.gz files are missing Content-Encoding: gzip headers. Ensure the [[headers]] blocks for soffice.wasm.gz and soffice.data.gz are in your netlify.toml.
Check Node version compatibility:
[build.environment]
NODE_VERSION = "20"
Enable asset optimization in Site settings → Build & deploy → Asset optimization.