Configure CORS on the bucket
This is the step people skip and then spend an afternoon debugging. Without it, uploads from the browser fail with a CORS error in the browser console, while server-side calls from Salesforce to S3 keep working. So the symptom is “uploads from the UI fail but admin diagnostics pass”, which is easy to misdiagnose.
The same GET rule also powers the photo editor. Large images, over Salesforce’s
4 MB server-side limit, load straight into the canvas from your bucket. Without GET
in the CORS rule, or if your Salesforce origin is not listed, large external images
open in view-only mode instead of being editable. Editing is capped at 50 MB.
Add the CORS rule
Section titled “Add the CORS rule”-
Open the bucket you created.
-
Go to the Permissions tab.
-
Scroll to Cross-origin resource sharing (CORS) and click Edit.
-
Paste the following JSON. Replace the entries in
AllowedOriginswith your actual Salesforce org domains, listing every domain users will upload from: production org, sandboxes, and Experience Cloud sites.[ { "AllowedHeaders": ["*"], "AllowedMethods": ["GET", "PUT", "HEAD"], "AllowedOrigins": [ "https://your-org.lightning.force.com", "https://your-org.my.salesforce.com", "https://your-org.sandbox.my.salesforce.com", "https://your-experience-site.my.site.com" ], "ExposeHeaders": ["ETag"], "MaxAgeSeconds": 3000 } ] -
Click Save changes.
Finding your Salesforce domains: in Salesforce Setup → My Domain, the Current My Domain URL and Lightning URL are the two you need for the production org. Sandboxes have their own equivalents. Experience Cloud sites appear in Setup → Digital Experiences → All Sites.
If you develop in scratch orgs, add those domains too. They look like
https://<name>-dev-ed.scratch.lightning.force.com and must be listed explicitly,
because S3 CORS does not support wildcards for subdomains.
Next: Create the access key.