Protect ranges and sheets
range.protect() creates a protection on that range (sheet.protect() for a whole tab); by default everyone keeps edit rights, so call protection.removeEditors(protection.getEditors()) and then addEditor for the people allowed. setWarningOnly(true) shows a warning instead of blocking. Only owners and editors with permission can protect.
Syntax
const p = range.protect().setDescription(text); p.removeEditors(p.getEditors()); p.addEditor(email)| Part | What it is |
|---|---|
setDescription(text) | Label shown in the Protected sheets and ranges panel |
removeEditors(emails) | Removes edit permission for the listed users; the owner always keeps it |
addEditor(email) / addEditors(emails) | Users who may edit the protected cells |
setWarningOnly(true) | Warn before editing instead of blocking |
setUnprotectedRanges(ranges) | On a sheet protection, leave some ranges editable |
Examples
const sheet = SpreadsheetApp.getActiveSheet();
const p = sheet.getRange("A1:C1").protect().setDescription("Headers");
p.removeEditors(p.getEditors());
if (p.canDomainEdit()) p.setDomainEdit(false);Result: Only the owner (and the script runner) can edit A1:C1
const p = sheet.getRange("E2:E100").protect().setDescription("Approvals");
p.removeEditors(p.getEditors());
p.addEditor("manager@example.com");Result: The manager and the owner can edit the approval column; others are blocked
const p = sheet.protect().setDescription("Locked report");
p.setUnprotectedRanges([sheet.getRange("B2:B10")]);Result: Whole tab locked except the input cells B2:B10
sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE).forEach(p => {
if (p.getDescription() === "Approvals") p.remove();
});Result: Removes the Approvals protection
Common mistakes
- The protection exists but everyone can still edit: you forgot removeEditors(getEditors()); a fresh protection keeps all current editors.
- You do not have permission to protect: only the spreadsheet owner or editors who were not restricted can create protections; the script runs as the current user.
- Running the script twice stacks duplicate protections: check getProtections first and reuse or remove the old one by description.
- Editors from your Workspace domain still edit: domain-wide edit access is separate; call setDomainEdit(false) when canDomainEdit() is true.
Practice Protect ranges and sheets
These drills check your answer on live data, free, no account needed.
- Pick some keysReturn a new object with only the listed `keys` of `obj`. Skip keys that `obj` does not have, but keep keys it has even when the value is `0`, `false`, `null`, `""` or `undefined`. Do not change `obj`.
Questions
Can a protected range stop the script itself from writing?
No. Scripts run with the permissions of the user who runs them (or the trigger owner), so a protection does not block a script run by the owner.
How do I protect every sheet in the file?
Loop ss.getSheets() and call sheet.protect() on each, removing editors the same way. Keep one unprotected input tab if people need to enter data.
Related
- Clear and format cellsClear and format Google Sheets cells with Apps Script: clear content or formats, set background and font color, bold, number and date formats, borders.
- onEdit triggerUse the onEdit trigger in Google Apps Script: the event object with range, value and oldValue, limiting it to one sheet or column, timestamps, installable.
- getActiveSheet vs getSheetByNameChoose between getActiveSheet and getSheetByName in Google Apps Script: when the active sheet is unreliable, targeting a tab by name, and missing sheets.
- Installable triggersInstallable triggers in Google Apps Script: how they differ from simple triggers, creating them from the UI or ScriptApp.newTrigger, onChange, onFormSubmit.
Previous: getActiveSheet vs getSheetByNameNext: Clear and format cells