Every XLSX carries a small, almost invisible directory of references called defined names. Most users only ever see them when typing a friendly name into a formula, but the workbook XML stores far more — hidden names invisible to the Name Manager, references to closed external files on a colleague’s laptop, pointers to deleted sheets, built-in flags from features the user no longer remembers turning on, and labels that quietly explain what the workbook is for. This post walks through where defined names live inside the XLSX, how to read the symbol table the way Excel reads it, what the most common leaks look like in practice, and how to actually clean named-range metadata before a workbook leaves your organization.
A defined name is a label attached to a value, a cell range, a formula, a constant, or a chunk of code, scoped either to the workbook as a whole or to a single worksheet. When a user types =TaxRate in a cell, Excel looks up TaxRate in the workbook’s symbol table and substitutes whatever expression is registered there — possibly Sheet1!$B$2, possibly the constant 0.075, possibly a long external reference into another file.
From the user’s perspective, defined names are a convenience: friendlier formulas, easier auditing, fewer hard-coded ranges. From the file’s perspective, the symbol table is one of the most expressive metadata layers in the entire XLSX. Each entry can carry the name itself (often a label that describes intent), an arbitrary expression (often pointing somewhere the recipient was never meant to see), a scope, a comment, a hidden flag, and a flag identifying it as a function or future-function reference.
The Name Manager in Excel’s ribbon shows visible defined names, but it hides built-in names, names with the visible="false" attribute, and many add-in-managed names. The list of names a recipient can read by unzipping the file is consistently larger — often dramatically larger — than the list the author saw before pressing Save.
Defined names are written to a single element inside xl/workbook.xml: <definedNames>. Each child <definedName> represents one entry in the symbol table.
// Excerpt from xl/workbook.xml
<definedNames>
<definedName name="TaxRate">Inputs!$B$2</definedName>
<definedName name="_xlnm.Print_Area" localSheetId="0">Sheet1!$A$1:$F$48</definedName>
<definedName name="InternalCostBasis" hidden="1">BackEnd!$D$10:$D$200</definedName>
<definedName name="OldQ4Forecast">'[Q4_Forecast_v17_DRAFT.xlsx]Summary'!$B$5</definedName>
</definedNames>
The element is a single flat list. There is no folder hierarchy and no per-name visibility tree — just attributes on each entry. The interesting attributes are:
| Attribute | Meaning |
|---|---|
| name | The label the user (or Excel) chose. Names beginning with _xlnm. or _xlfn. are reserved. |
| localSheetId | If present, scopes the name to a single worksheet (zero-indexed). If absent, the name is workbook-scoped. |
| hidden | 1 hides the name from the Name Manager. The name still resolves in formulas and is fully visible in the XML. |
| function | Marks the name as a user-defined function (typically a VBA Function in vbaProject.bin). |
| comment | Free-text note from the author, often more candid than anything in the cells themselves. |
| vbProcedure | 1 if the name resolves to a VBA procedure rather than a range. |
| publishToServer | A flag for Excel Services / SharePoint publishing. Hints the workbook was built for a specific server pipeline. |
The text content of each <definedName> element is the refersTo expression: the formula or reference the name evaluates to. Reading those expressions across all names in a workbook is one of the fastest ways to get a map of how the workbook is wired internally and where it touches the outside world.
The hidden="1" attribute is exactly what it sounds like — a single flag that omits the name from the Name Manager dialog. Hidden names typically appear when:
DiscountTier1, SalesTeamCommission, or _HighWaterMark behind hidden="1", believing those entries are now invisible._xlnm._FilterDatabase and similar names, and the Data Model adds its own hidden symbols.The hidden="1" attribute is one byte in the XML. The name, the refersTo expression, and any comment are right next to it as plaintext. Anyone who unzips the XLSX and opens xl/workbook.xml in a text editor reads every hidden name in the workbook in seconds. Hiding a name does not encrypt it; it just stops the Excel UI from listing it.
_xlnm. and What They RevealNames beginning with the prefix _xlnm. are reserved by Excel for built-in features. They are not shown in the Name Manager (the UI suppresses anything starting with the underscore prefix), but they sit in the XML alongside user-created names. The most common built-ins:
| Built-in Name | What It Records |
|---|---|
| _xlnm.Print_Area | The exact cell range a user marked for printing — often narrower than the data, because the author cropped to what they intended to publish. |
| _xlnm.Print_Titles | Header rows / columns the author wanted repeated on each printed page. |
| _xlnm._FilterDatabase | The range AutoFilter currently applies to. Exposes which sheet ranges are treated as “the data” even when the rest of the sheet is decorative. |
| _xlnm.Criteria | Named criteria from advanced filters — reveals filter logic the author thought was transient. |
| _xlnm.Database | Legacy “data form” range marker; often points to an older, larger dataset that the visible sheet has since been narrowed from. |
| _xlnm.Sheet_Title | Sheet caption metadata, occasionally used to embed an internal title that differs from the visible tab name. |
| _xlnm.Extract | Records the most recent advanced-filter extract destination — sometimes a sheet the author later deleted. |
Several built-ins are scoped per worksheet via localSheetId, so a workbook with twelve sheets can carry twelve copies of _xlnm.Print_Area, each with its own range. Reading them as a group is a fast tour of how the author imagined each sheet would look on paper — often narrower than the on-screen data, often pointing to ranges that contain rows the published version was supposed to omit.
_xlfn. and _xlfn._xlws. Prefixes: Version Fingerprints in DisguiseWhen a workbook uses a function that did not exist in older Excel versions — XLOOKUP, FILTER, LET, LAMBDA — Excel does not just store the function call. It also writes the function name into formulas with a _xlfn. or _xlfn._xlws. prefix so that older Excel versions can fall back to a cached value rather than crash on the unknown function.
These prefixes are not stored in the <definedNames> element directly — they live in worksheet formulas — but they form a parallel symbol table that gives away the same kind of information. A recipient grepping for _xlfn.LAMBDA across xl/worksheets/ learns the workbook was authored on a Microsoft 365 build with LAMBDA support, which narrows the author’s Excel version dramatically.
// Excerpt from xl/worksheets/sheet1.xml — a formula leaking version
<c r="G7">
<f>_xlfn.XLOOKUP(F7,Lookups!$A$2:$A$200,Lookups!$B$2:$B$200,"n/a")</f>
<v>42</v>
</c>
Combined with the application fingerprint in docProps/app.xml and the build number in docProps/core.xml, the function prefix narrows the author’s tool stack to a remarkably specific identifier — useful in forensics, occasionally awkward when sharing externally with a vendor that should not know which monthly Office build a customer is on.
A defined name’s refersTo expression can point outside the workbook. Three flavors of external reference appear in the symbol table, each with its own metadata payload:
A name pointing to '[Q4_Forecast_v17_DRAFT.xlsx]Summary'!$B$5 records the source workbook’s filename and the sheet inside it. If the original was on a network share, the path is even longer:
'\\fileserver\Finance\Models\2026\[Forecast_v17_DRAFT.xlsx]Internal'!$B$5
Server hostname, department folder, year, draft suffix, and the “Internal” sheet name all leak through a single name entry.
When a workbook references a closed file via externalLinks, the linked workbook is registered in xl/externalLinks/externalLink1.xml and named in _rels/workbook.xml.rels. Defined names then refer to [1]Summary!$B$5 by index rather than by full path. Reading the symbol table without also reading the externalLinks part hides the actual filename, but reading both together produces a complete map. (Our prior post on External Links covers the externalLinks side in depth.)
Defined names sometimes reference web queries through Power Query connections or older “web data” features. The URL embedded in the connection becomes part of the workbook’s metadata, often pointing to internal endpoints (intranet pages, dashboards, REST URLs with embedded API keys). The symbol-table entry is brief; the connection it depends on, in xl/connections.xml, is verbose.
Defined names persist in the workbook even after the cells, sheets, or external files they refer to are deleted. Excel does not silently remove an orphaned name; the Name Manager keeps it, marks the refersTo as #REF!, and the entry remains in the XML.
// Phantom names left over from deleted sheets and ranges
<definedName name="Q4DraftLayoffPlan">#REF!</definedName>
<definedName name="LegalReviewComments">#REF!</definedName>
<definedName name="BoardScenario_DoNotShare">#REF!</definedName>
Even when the cells, sheet, or source file are completely gone, the name is still in the XML. A reader does not get the data the name pointed to, but they get a long descriptive label that often summarizes what was there: PreLayoff_HeadcountByTeam, Q1_Customers_PaymentsOverdue_DRAFT, M&A_Target_FairValue_2025. The name itself is the leak.
A defined name does not have to refer to a cell range. The refersTo expression can be a literal value, an array constant, or an arbitrary formula. This is convenient for the author and informative for the reader.
// Names that store values directly, not just references
<definedName name="DiscountTable">{1000,0.05;5000,0.10;25000,0.18;100000,0.25}</definedName>
<definedName name="ApiKeyHash">"sha256:5d41402abc4b2a76b9719d911017c592"</definedName>
<definedName name="CommissionFormula">=IF(Sales>Quota*1.2,0.12,IF(Sales>Quota,0.08,0.04))</definedName>
<definedName name="_TaxJurisdictions">{"CA","0.0725";"NY","0.04";"TX","0.0625"}</definedName>
These “value names” are particularly common in template-driven workflows and in models that started life as hidden constants someone wanted to keep out of the visible sheet. Once a value is in the symbol table, the visible sheet can be cleared, copied, or replaced — the value persists. Removing the cells is not the same as removing the name.
When a workbook contains VBA, defined names can carry a vbProcedure="1" attribute and a refersTo expression that names a VBA procedure rather than a range. Excel uses these to wire shapes, form controls, and event handlers to code.
The procedure name itself often telegraphs the workbook’s purpose: SubmitForReview, PostToServer, EncryptAndUpload, SendCommissionEmails. A reader who never opens the VBA editor still gets a coarse map of what code lives behind the surface and what external systems it might talk to.
In incident response, the list of procedure-bound defined names is often the first thing investigators read. It identifies entry points, callbacks bound to UI elements, and modules that interact with the network — without needing to parse the VBA bytecode at all. For attackers, the names are also a quick reconnaissance signal: SendInvoicesViaSMTP tells them where the credentials probably are.
You can audit defined names without ever opening Excel. The XML is plain text inside the ZIP. Three approaches that scale from one-off inspection to bulk pipelines:
# 1. Quick command-line audit
unzip -p workbook.xlsx "xl/workbook.xml" | \
xmllint --xpath '//*[local-name()="definedName"]' -
# 2. Find every external reference in the symbol table
unzip -p workbook.xlsx "xl/workbook.xml" | \
grep -oE '\\\\[^<]*|\[[^<]*\]'
# 3. List names with hidden="1"
unzip -p workbook.xlsx "xl/workbook.xml" | \
grep -oE 'definedName[^>]*hidden="1"[^>]*'
For programmatic processing, openpyxl exposes the symbol table directly:
# Python: enumerate every defined name and flag interesting ones
from openpyxl import load_workbook
wb = load_workbook("workbook.xlsx", read_only=True)
for name in wb.defined_names.values():
flags = []
if name.hidden: flags.append("HIDDEN")
if name.localSheetId is not None: flags.append(f"sheet={name.localSheetId}")
if "[" in (name.value or ""): flags.append("EXTERNAL")
if "#REF" in (name.value or ""): flags.append("ORPHAN")
print(name.name, name.value, flags)
Running this against a typical corporate financial model often produces a list with three to five times more names than the Name Manager showed the author — a dramatic gap between what the user thought they were sharing and what the file actually contains.
In aggregate, the symbol table is a remarkable cross-section of the workbook’s history and the author’s context. A non-exhaustive list of the kinds of leaks that come up over and over in real audits:
\\corp-fs01\Finance\, https://intranet.example.com/team-sales/, OneDrive sync paths with usernames in them.Project_Phoenix_Q3, RipcordFinalRecon, Falcon_v3_pricing. Names rarely get sanitized before sharing.CFO_ApprovedDiscount, VP_OverrideThreshold, CEO_FinalSay.PreLayoff_Headcount, Confidential_Customer_Costs, Litigation_Sched_Q2, all marked #REF! but with names intact.Margin_TooLow_DoNotSend, Risk_RedoBeforeAudit, Sales_DoNotShareWithOps.v1 through v17_FINAL_FINAL_v2.The Document Inspector in Excel does not remove most defined names. It removes some hidden names tied to specific features (like Power Query queries) but leaves user-created hidden names, orphaned references, and built-in print-area / filter-database names in place. Cleaning the symbol table requires explicit work.
The Name Manager (Formulas → Name Manager) shows visible names. To see hidden ones, run a quick VBA snippet: For Each n In ActiveWorkbook.Names: n.Visible = True: Next. Then re-open the Name Manager and review every entry.
Filter to #REF! and to refersTo expressions containing [ or \\ or http. Delete the ones the workbook does not actually need. Test the workbook still calculates afterward.
Rename labels that describe internals: CFO_ApprovedDiscount → DiscountFloor, Margin_TooLow_DoNotSend → delete entirely if not needed. The cell formulas may need to be re-pointed; that is the point.
For each sheet, clear the print area (Page Layout → Print Area → Clear Print Area) and turn off AutoFilter. Both actions remove the corresponding _xlnm. entries on that sheet.
A short Python script can wipe every name in one pass:
from openpyxl import load_workbook
wb = load_workbook("in.xlsx")
for n in list(wb.defined_names):
del wb.defined_names[n]
wb.save("out.xlsx")
This is destructive: any formula that referred to a deleted name now resolves to #NAME?. Use it only on a copy intended as a final, locked-down deliverable, ideally one converted to values-only first.
Run this checklist against any workbook leaving the organization with named ranges or formulas:
#REF!) names whose label alone reveals deleted content?\\server\, drive letters, OneDrive sync paths, URLs)?[N] indices, and have I cross-checked _rels/workbook.xml.rels to see what file [N] actually was?CFO_*, Internal_*, DoNotShare_*, Layoff_*, project codenames)?_xlnm.Print_Area entries that crop to internal-only ranges?_xlnm._FilterDatabase entries that point to internal data ranges?vbProcedure="1") whose procedure names describe sensitive code paths?<definedNames> element programmatically?The symbol table is one of the most informative metadata layers in an XLSX, and one of the least audited. The Name Manager hides as much as it shows; the underlying XML hides nothing. A reader who unzips the workbook and reads xl/workbook.xml walks away with a map of the workbook’s history — deleted sheets named in stranded entries, external workbooks identified by their full network paths, internal review labels in plain text, function-prefix fingerprints pinning down the author’s Office build, and procedure-bound names sketching out what the VBA does without parsing a byte of it.
Defined names are useful, and most workbooks benefit from a thoughtful symbol table. Treating that table as a public-facing artifact — pruning it, sanitizing labels, eliminating orphaned references, removing names that point outside the file — is what separates a workbook the recipient was meant to read from a workbook the recipient will read in ways the author never expected.
How named-range external references are resolved through the externalLinks part.
Where every metadata layer lives inside an XLSX, including the symbol table.
Why “hidden” in the workbook XML is a UI hint, not a confidentiality boundary.