Deployment
View SourceTo host a Popcorn app, you need to configure your web server.
Set cross-origin isolation headers
Popcorn requires SharedArrayBuffer to work. This buffer will work on the page if you send the following headers with the page response:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpIn development, both mix popcorn.dev and the Vite plugin set them.
Set response metadata
- Serve
.wasmfiles withContent-Type: application/wasm. - Set
Content-Encoding: gziporContent-Encoding: brto serve compressed assets. Popcorn generates them automatically.bris Brotli compression and works better than Gzip. Compression level is configurable in the packager. SetVary: Accept-Encodingwhen selecting a representation.
Cache bundles independently
The packager emits three archives: stdlib.<hash>.tar for selected Erlang/Elixir
built-ins, app.<hash>.tar for user code, and deps.<hash>.tar for dependencies.
Each filename contains the SHA-256 of its final uncompressed contents. The boot script also has a content hash in its filename. Tree-shaking runs on all applications which means a change in your app may include or drop a module from a standard library and change the hash.
Serve hashed tarballs with:
Cache-Control: public, max-age=31536000, immutableThis prevent re-requesting them if they didn't change. They're referenced by manifest.json which should be checked periodically for using new hashed tarballs.
Avoid transforming the output
Popcorn relies on Web Workers, which need stable paths for the scripts they execute. Popcorn's packager is configured to output the correct file layout. Keep that layout when deploying.
Use beam.otpAssetsRoot if you want to run it on subpage. The value must end with /.
Configure the Content Security Policy
Popcorn.JS.run/3 currently evaluates JavaScript source. The page Content
Security Policy must permit unsafe-eval. Popcorn initialization will fail if it's not allowed.
Sample nginx config for hosting Popcorn applications
This example is based on the nginx configuration used for Popcorn's landing page.
Save it as /etc/nginx/nginx.conf, replace example.com with your domain, and copy
your application's generated files to /usr/share/nginx/html.
It listens on HTTP port 80. Use it behind a proxy that terminates HTTPS, or configure TLS in nginx. Browsers require HTTPS for cross-origin isolation, except on localhost.
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
map $uri $popcorn_cache_control {
default "no-cache";
"~\.[a-f0-9]{64}\.(tar|boot)$" "public, max-age=31536000, immutable";
}
server {
listen 80;
listen [::]:80;
server_name example.com;
port_in_redirect off;
root /usr/share/nginx/html;
index index.html;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Resource-Policy "cross-origin" always;
add_header Cache-Control $popcorn_cache_control;
gzip_static on;
gzip_vary on;
gzip_proxied any;
location ~ \.(wasm|mjs|tar|boot|json)$ {
types {
application/wasm wasm;
application/javascript mjs;
application/json json;
}
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
}
}gzip_static
serves generated .gz files when the client accepts gzip and sets Content-Encoding
automatically. Your nginx build must include ngx_http_gzip_static_module.
This example uses gzip; serving .br files requires an additional Brotli module.
If your application uses client-side routing, replace try_files $uri $uri/ =404;
with try_files $uri $uri/ /index.html;. Keep the asset location unchanged so missing
runtime files, manifests, and archives return 404.
Validate the configuration with nginx -t before reloading nginx.