Stripping Sections from a Wasm Binary
This page answers one task: a release .wasm file is larger than expected, and inspection shows sections that do not affect execution — function names, debug
information, toolchain metadata. You want to remove what users do not need, keep what you need for debugging production problems, and avoid removing anything that
breaks the module.
Prerequisites
- [ ] The release module and a way to rebuild it.
- [ ]
wasm-objdump -horwasm-tools objdumpto list sections, and at least one stripping tool. - [ ] A plan for production debugging (symbol storage for stack traces).
Standard sections versus custom sections
A module’s known sections — type, import, function, table, memory, global, export, start, element, data count, code, data — define its behaviour and must not be removed. Custom sections (id 0) carry a name and arbitrary bytes, which engines ignore for execution (with a few exceptions that change behaviour in toolchains, not engines). Toolchains emit several kinds:
name— function, local and other names. Makes stack traces and profiles readable; often 5–20% of an unstripped module.- DWARF sections (
.debug_info,.debug_line, …) — full source-level debug information; can exceed the size of the code itself. producers— which languages and tools built the module; small.target_features— features the object was compiled with; used by linkers, not engines; small.sourceMappingURLandexternal_debug_info— pointers to separate debug files.- Toolchain-specific sections such as
dylink.0(dynamic linking metadata) or component-related sections — which are required by their consumers.
Step 1 — see what is there
wasm-objdump -h app.wasm
# Custom start=... size=0x0005e3f0 ".debug_info"
# Custom start=... size=0x0001a2c4 "name"
# Custom start=... size=0x00000043 "producers"
wasm-tools objdump app.wasm # alternative listing with sizes
Add up custom section sizes to see what stripping can save.
Step 2 — strip with the right tool
wasm-strip app.wasm # WABT: removes all custom sections in place
wasm-opt -O3 --strip-debug --strip-producers app.wasm -o app.min.wasm # Binaryen: selective
wasm-tools strip app.wasm -o app.min.wasm # removes custom sections; options to keep some
wasm-opt --strip-debug removes DWARF and the name section; --strip-producers removes the producers section. In Rust, strip = true (or "symbols") in the
Cargo release profile removes names and debug info at link time; wasm-pack and Emscripten have their own options (Emscripten removes most custom sections at
-O2+ unless debug flags are given). Know what your pipeline already does before adding another step.
Step 3 — keep what you need for debugging
Stripping names makes production stack traces show wasm-function[1234] instead of function names. Before stripping, save what you need: keep the unstripped
module (or just its name section) in your artefact storage, keyed by the stripped module’s content hash, and use it to symbolicate reported stack traces. For full
source-level debugging of production builds, emit DWARF into a separate file and reference it with an external_debug_info section, which DevTools can follow
when you have access to the file.
Step 4 — do not strip required sections
Some custom sections are part of a contract with a loader rather than metadata: dylink.0 in dynamically linked side modules tells the loader how much memory and
table space the module needs; component-model binaries use custom sections during componentisation (wit-bindgen’s component type sections in core modules before
wasm-tools component new); some toolchains read target_features during linking. Strip only final artefacts, after linking and componentisation, and test
the stripped file exactly as shipped.
Step 5 — measure the result
Compare raw and compressed sizes before and after. DWARF often dominates raw size but compresses well; the name section is moderate either way. Report the compressed difference, which is what users download.
Names: strip or keep?
Keeping the name section costs some kilobytes compressed and makes every production stack trace, profile and error report readable without extra infrastructure. Stripping it saves those kilobytes but requires a symbolication pipeline. For small teams without such a pipeline, keeping names is often the better trade; for large modules where every kilobyte matters, strip and symbolicate.
Splitting DWARF into a separate file
Rather than choosing between full debug information and a small release file, ship both: the small file to users, the debug information to developers only.
Emscripten supports this with -gseparate-dwarf, which writes DWARF to a separate .debug.wasm file and adds an external_debug_info custom section to the main
module pointing at it; Chrome’s C/C++ DevTools extension follows that pointer when the file is reachable (for example from an internal server or a local path
mapping). For other toolchains, wasm-opt and wasm-tools can split or extract sections, and you can archive the original unstripped module as the debug
companion. Keep the debug file’s URL private if the source structure is sensitive: DWARF contains file paths, function names, variable names and types, which is
more than many teams want public.
Automating the policy in the build
Decide the policy once and encode it: which sections the shipped module keeps, which are archived, and where. Then enforce it in CI with a check that lists the
shipped module’s sections and fails if anything unexpected is present — DWARF sneaking into a release because someone changed a profile, or a required section
like dylink.0 missing from a side module. Archive the debug artefacts with the release’s content hash as the key so error-reporting tools can fetch them
automatically, and test symbolication with a deliberately triggered trap in a staging build, so you know the pipeline works before you need it.
Privacy and intellectual property
Names and debug information reveal how code is organised. For most open-source or client-side code that is harmless — the logic ships anyway — but some teams prefer not to publish internal names. Stripping the name section removes the easy view; it does not make the code secret, since the instructions remain and can be decompiled. Treat stripping as a size and tidiness measure, not as protection.
Custom sections you add yourself
Teams sometimes embed their own metadata — a build ID, a licence notice, a feature list. Keep such sections small and stable, and remember that blanket stripping tools remove them too; use selective stripping if they must survive.
Verifying a stripped module behaves identically
Stripping should never change behaviour, and a quick check proves it: run the test suite against both the unstripped and stripped files, and compare a hash of the non-custom sections of both files, which must be identical. Any difference means a tool did more than strip.
Expected output
The release module drops from 3.8 MB to 1.1 MB raw by removing DWARF and names (from 690 KB to 520 KB with Brotli); the unstripped artefact is archived by content
hash for symbolication; dylink.0 is kept in side modules; and the shipped file passes the same tests as the unstripped build.
Gotchas
- Stripping before linking or componentisation. Required metadata disappears. Strip final artefacts.
- Losing symbols. Production traces become indices. Archive unstripped builds.
- Double stripping steps. Pipelines already strip. Check before adding tools.
- Removing
dylink.0. Side modules fail to load. Keep loader sections. - Measuring only raw size. Users download compressed bytes.
- Blanket stripping of your own metadata. Build IDs disappear too. Strip selectively when needed.
Performance note
Stripping does not change execution speed, but smaller files download and compile slightly faster; removing 2.7 MB of DWARF cut download time on a 10 Mbit/s link by about 0.15 s after compression.
Frequently Asked Questions
Do engines use the name section? Only for diagnostics — stack traces and profiles show names when present.
Is stripping reversible? No — keep the unstripped artefact if you might need it.
Does stripping affect WebAssembly.Module.customSections?
Yes — stripped sections are no longer available to code that reads them.
Should I strip in development builds? No — names and debug info make debugging possible.
Can I ship a small module but keep source-level debugging? Yes — split DWARF into a separate file referenced by external_debug_info and keep that file private.
Does stripping protect my code? No — instructions remain and can be decompiled; stripping saves size and hides names only.
How do I make sure releases never ship DWARF? List the shipped module’s sections in CI and fail on anything outside the agreed policy.
Where should debug artefacts be stored? In private artefact storage keyed by the release module’s content hash, so error tools can fetch them.
How do I prove stripping changed nothing functional? Run the tests on both files and compare a hash of their non-custom sections, which must match.
Related
- Reading the name custom section — what names contain.
- Adding custom sections to a Wasm binary — the reverse.
- Symbolicating Wasm stack traces in production — using archived names.
- Debugging Wasm with DWARF source maps — debug info.
← Back to Wasm Binary Format Deep Dive