Settings

An .xlsx is a zip, and the sheets are only some of it

Rename a copy of a workbook to .zip and open it. A small test workbook built for this page holds these parts, among others:

xl/workbook.xml the sheet list, and the defined names xl/worksheets/sheet1.xml one part per sheet, visible or not xl/sharedStrings.xml every distinct piece of text in the book xl/calcChain.xml which cells hold formulas xl/comments1.xml cell notes, with the author on each one xl/pivotCache/pivotCacheDefinition1.xml xl/pivotCache/pivotCacheRecords1.xml docProps/core.xml author, last saved by, revision count docProps/app.xml editing time, company

Deleting something from the grid is a change to one of those parts. Several of the others are not touched by it.

Hidden, and the one nobody has heard of

Every sheet in the book appears in xl/workbook.xml as a <sheet> element with a name and a relationship id. A hidden sheet has one more attribute:

<sheet name="Summary" sheetId="1" r:id="rId1"/> <sheet name="Working" sheetId="2" state="hidden" r:id="rId2"/> <sheet name="Payroll" sheetId="3" state="veryHidden" r:id="rId3"/>

The attribute takes three values: visible, hidden and veryHidden. The first two behave as you would expect. A veryHidden sheet does not appear in the Unhide list, so nobody browsing the workbook in Excel finds it from the interface at all; it takes the VBA editor or unzipping the file. It was never meant as a security feature, and is routinely treated as though a sheet marked that way is gone.

It is not gone. The sheet's own part is still in the zip with every cell in it. X-RAY reports both of the test workbook's hidden sheets by name, and marks the second as very hidden.

The shape of data that was removed

Hidden rows and columns are one attribute each: <row r="4" hidden="1"> and a <col> entry with hidden="1" on it. The cells inside them are ordinary cells with ordinary values.

Two more things describe data that is no longer visible. The autofilter is stored as <autoFilter ref="A1:D40"/>, a range written when the filter was applied, and the print area is a defined name called _xlnm.Print_Area. Either can name a rectangle far larger than anything still on the sheet, which tells a reader how many rows there used to be.

Ordinary defined names do the same more directly. The test workbook carries two:

<definedName name="Redundancy_List">Payroll!$A$1:$D$40</definedName> <definedName name="Old_Bonus_Pool">Working!$F$1:$F$99</definedName>

The range may be long gone. The name is a sentence about what used to be in it, and it is sitting in xl/workbook.xml.

Text survives the cell it was in

Excel does not store text in the cell. It keeps one pool of strings for the whole workbook in xl/sharedStrings.xml, as a list of <si> items, and a cell that holds text is written as a type-s cell whose value is a position in that list:

<c r="A2" t="s"><v>3</v></c> means: the text at position 3 in sharedStrings

Clearing a cell removes the reference. Whether the entry itself goes depends on whether the application rebuilds the pool, and a pool item that no cell points at any more is still a valid file. The test workbook has one on purpose: item 8 reads Redundancy shortlist: Okafor, Beltran, Doyle, no cell in any sheet refers to it, and it opens in a spreadsheet application without complaint and shows nothing.

The same pool is shared with the sheets you deleted. This is exactly why CLEANSHEET rebuilds the table rather than leaving it: on the test file it walks the surviving sheets, keeps only the items they still reference, renumbers every cell's index to match, and the pool goes from nine items to six.

A number outlives the formula that made it

A formula cell holds two children: the formula and the last value it produced.

<c r="C2"><f>Payroll!D40*1.15</f><v>1264350</v></c>

The <v> is the cached result, kept so the file can be displayed without recalculating first. Remove the <f> and the number stays. That is deliberate when CLEANSHEET does it: the visible figures are untouched and the workings, including the reference to a sheet you would rather nobody knew about, come out.

Read the other way round, a cached value is evidence. A number computed from a sheet that is no longer in the workbook is still the number, and the formula beside it names the sheet, the cell and the multiplier.

xl/calcChain.xml is a companion to all this: a list of which cells hold formulas, by cell reference and sheet index. CLEANSHEET deletes it, because once the formulas are gone it would name cells that no longer calculate and Excel reads that as a damaged file. Excel builds it again on the next save.

The pivot cache, which is the one that catches people

A pivot table does not read the source rows when it draws itself. It reads a cache: a private copy of the source data, written into the workbook as its own parts. That is what makes a pivot fast, and what lets it keep working when the source is not open.

The copy is in two files. xl/pivotCache/pivotCacheDefinition1.xml names the source in a <worksheetSource> element, then lists every column as a <cacheField> with a <sharedItems> list of every distinct value in that column. xl/pivotCache/pivotCacheRecords1.xml then holds one <r> element per source row, with an <x> index into the shared items for text and an <n> for a number.

The pivot table part itself refers to the cache by cacheId. It never names the sheet. The link runs table to cache, and the cache remembers the sheet — which means deleting the sheet breaks nothing and removes nothing.

This was tested rather than assumed. The test workbook has a pivot table over a very hidden Payroll sheet of names, teams and salaries. The Payroll worksheet part was then deleted from the zip, along with its entry in the sheet list, its relationship and its defined name. Opened afterwards, the workbook has two sheets and no Payroll. Its pivot cache still says:

<worksheetSource ref="A1:D40" sheet="Payroll"/> refreshedBy="m.hollis" recordCount="4" saveData="1" Name Team Salary Priya Raman Legal 141500 Tom Okafor Legal 98250 Ana Beltran Finance 177000 Ken Doyle Finance 112400

Those four rows were read back out of the file by a standard spreadsheet library, not carved out by hand. The sheet is gone and the salaries are not. refreshedBy adds the name of whoever last refreshed the pivot.

What to do before you send a workbook

Look at it first. X-RAY unpacks the zip in the browser tab and reports the author, the account that saved it last, the revision count, the editing time, the company, and every hidden and very hidden sheet by name.

Then clean it. CLEANSHEET is the spreadsheet pass. On the test workbook it removed both hidden sheets and their parts, both defined names that pointed at them, the pivot table and both pivot cache files, xl/calcChain.xml, the comments part and the drawing that renders the note boxes; it froze the one formula to its cached value; and it rebuilt the shared string pool down to the six items still in use. It keeps the print area, because that one still refers to a sheet that is still there.

Check what the numbers are about. DATASCAN reads a CSV or JSON export and names the columns that look like personal data — emails, phone numbers, national insurance and social security numbers, card numbers, addresses — which is the question of whether the data should be leaving at all, rather than where it is hiding.

And check for credentials. SENTINEL reads every XML part inside the zip, not only the visible cells, so a key or a token sitting in a hidden sheet, an orphaned shared string or a pivot cache is in scope. It reports on Office files; it does not rewrite them.

What this is, and what it is not

CLEANSHEET does not touch the document properties. After a full pass over the test workbook, docProps/core.xml still said author Priya Raman, last saved by m.hollis and revision 34, and docProps/app.xml still said 412 minutes of editing and the firm's name. That is SCRUB's job, and it is a separate run.

Hidden rows, hidden columns and the filter range stay. The pass removes hidden sheets, not hidden rows. On the test file the row marked hidden="1", the hidden column and the autoFilter ref="A1:D40" were all still there afterwards. Unhide everything and look before you send, because a hidden row is one attribute away from being read.

Freezing formulas changes the workbook. Every <f> comes out and the cached <v> stays, so the figures are identical and nothing recalculates ever again. That is the right thing for a copy going out of the building and the wrong thing for the working file. Clean a copy.

DATASCAN reads an export, not the workbook. It takes CSV, TSV and JSON. Point it at a sheet you have exported and it will tell you which columns carry personal data; it does not open the .xlsx and cannot see the hidden sheets inside one. Use it on what you are about to hand over, alongside the cleaner on the file itself.

Reporting is not removing. X-RAY reports hidden sheets but not the pivot cache, the shared string pool or the cached values, and changes nothing. SENTINEL reads every part but is looking for secrets specifically, and does not rewrite Office files. The tool that actually alters the workbook is CLEANSHEET.

Questions people ask

What is a veryHidden sheet?

A worksheet whose state attribute in xl/workbook.xml is set to veryHidden rather than hidden. The difference is that it does not appear in Excel's Unhide list, so it cannot be brought back from the normal interface at all. The sheet and all of its data are still in the file.

Can someone see a spreadsheet's data after I delete the source sheet?

If there is a pivot table over it, yes. The pivot table keeps its own copy of the source rows in xl/pivotCache/pivotCacheRecords1.xml, and deleting the worksheet does not delete that part. This was tested on a workbook built for this page: with the source sheet removed from the zip, every source row was still read back out of the cache.

Why is deleted text still in sharedStrings.xml?

Because it is one pool of strings for the whole workbook, indexed by position, and cells refer to positions in it. Clearing a cell removes the reference. Whether the entry is dropped depends on whether the application rewrites the pool on save, and an entry nothing points at is a perfectly valid file that shows nothing on screen.

Does deleting a formula remove the number it worked out?

No, and that is usually what you want. A formula cell stores the formula and the last value it produced side by side, so removing the formula leaves the figure exactly as it was. It also means a number computed from a sheet that is no longer in the workbook stays behind after the sheet goes.

Is saving as CSV a safe way to strip all of this?

For one sheet, largely yes: a CSV is the values of one grid and nothing else, so hidden sheets, pivot caches, comments, the string pool and the document properties do not come with it. It also loses the formatting and every other sheet, and it does nothing about personal data in the rows themselves, which is a different question.

How do I check a workbook without opening Excel?

Copy it, rename the copy to .zip and unzip it. Open xl/workbook.xml and look at the state attribute on every sheet element and at the defined names. Then look at what parts are in the folder: anything under xl/pivotCache/ is a copy of somebody's source rows, xl/comments1.xml is the notes, and xl/sharedStrings.xml is every piece of text the book has ever held. It needs nothing installed and takes about a minute.

Related tools