SharePoint throws “The security validation for this page is invalid and might be corrupted” when a POST arrives without a valid form digest. Get a fresh one from /_api/contextinfo and send it in the X-RequestDigest header:
const site = "https://spguides.sharepoint.com/sites/SPGuides";
const ctx = await fetch(site + "/_api/contextinfo", { method: "POST", headers: { Accept: "application/json;odata=nometadata" } });
const digest = (await ctx.json()).FormDigestValue;
// send it on every POST, MERGE or DELETE
headers: { "X-RequestDigest": digest }
With that header, the same request returns 201 Created instead of 403 Forbidden.
In this tutorial, we’ll reproduce the security validation for this page is invalid error on a real SharePoint Online site, then fix every cause: a missing header, an expired digest, a digest from the wrong site, stale classic pages and server-side code.
What does “the security validation for this page is invalid” mean?
Every SharePoint write needs a form digest: a token tied to you, one site and a time window. It’s SharePoint’s protection against cross-site request forgery.
Create, update and delete calls send it in X-RequestDigest, unless the request carries an OAuth token. If it’s missing, expired or from another site, SharePoint rejects the call with HTTP 403. My SharePoint REST API tutorial covers the read side, which never needs it.
Reproduce the error with a REST POST
I made a small list called SPGDemo Projects on our SPGuides site, with Title and Status columns. Then I ran this in the browser console while signed in to the site. It’s a normal item create, minus the digest:
const siteUrl = "https://spguides.sharepoint.com/sites/SPGuides";
const res = await fetch(siteUrl + "/_api/web/lists/getbytitle('SPGDemo Projects')/items", {
method: "POST",
headers: {
"Accept": "application/json;odata=nometadata",
"Content-Type": "application/json;odata=nometadata"
},
body: JSON.stringify({ Title: "Vendor Contract Renewal", Status: "New" })
});
console.log(res.status, await res.text());
SharePoint answered with a 403 and this body:
403 {"odata.error":{"code":"-2130575251, Microsoft.SharePoint.SPException","message":{"lang":"en-US",
"value":"The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again."}}}
Error code -2130575251 is the digest check. The Back button advice comes from classic form pages, so ignore it for REST calls.
Fix 1: Get the digest from /_api/contextinfo and send X-RequestDigest
Ask the same site for a digest with an empty POST:
const ctx = await fetch(siteUrl + "/_api/contextinfo", {
method: "POST",
headers: { "Accept": "application/json;odata=nometadata" }
});
const info = await ctx.json();
console.log(info.FormDigestTimeoutSeconds, info.FormDigestValue);
It printed the lifetime in seconds and the digest itself (shortened here):
1800 0x4B2E...(128 hex characters)...3A092,24 Oct 2026 05:30:18 -0000
Now repeat the create with the header added:
const res2 = await fetch(siteUrl + "/_api/web/lists/getbytitle('SPGDemo Projects')/items", {
method: "POST",
headers: {
"Accept": "application/json;odata=nometadata",
"Content-Type": "application/json;odata=nometadata",
"X-RequestDigest": info.FormDigestValue
},
body: JSON.stringify({ Title: "Vendor Contract Renewal", Status: "New" })
});
const item = await res2.json();
console.log(res2.status, item.Id, item.Title, item.Status);
This time the console printed the status, ID, title and status of the new item:
201 8 'Vendor Contract Renewal' 'New'
Item 8, Vendor Contract Renewal, showed up in the list right away. The two rows above it came from the PowerShell runs later in this post:

Fix 2: Read #__REQUESTDIGEST in classic page scripts
Old Script Editor and Content Editor code often posts with jQuery. Classic pages with the default master page keep a valid digest in a hidden field, so add one header:
$.ajax({
url: siteUrl + "/_api/web/lists/getbytitle('SPGDemo Projects')/items",
type: "POST",
contentType: "application/json;odata=verbose",
data: J