Render/Cloudflare stripping CORS headers [from Strapi requests]

I am hosting Strapi v4 on Render, and during some operatins with Strapi’s Media Library, I am running into CORS errors.

Here is my full middlewares.js from Strapi,

module.exports = ({ env }) => {
  let validMediaSources = [
    "'self'",
    "data:",
    "cdn.jsdelivr.net",
    "strapi.io",
    `${env("AWS_BUCKET")}.s3.${env("AWS_REGION")}.amazonaws.com`,
    `${env("AWS_BUCKET")}.s3.amazonaws.com`, // Following [this](https://github.com/strapi/strapi/tree/master/packages/providers/upload-aws-s3#security-middleware-configuration) and [this](https://github.com/strapi/strapi/issues/11637#issuecomment-977244572), found we needed to add `${env('AWS_BUCKET')}.s3.amazonaws.com` noticing a CSP error in the Media Library view
    `api-powderhouse-org.onrender.com`,
  ];

  return [
    "strapi::errors",
    {
      // Configure middleware security to allow viewing thumbnails from S3
      // See [this](https://github.com/strapi/strapi/tree/master/packages/providers/upload-aws-s3#security-middleware-configuration) for more
      name: "strapi::security",
      config: {
        contentSecurityPolicy: {
          directives: {
            "script-src": ["'self'", "'unsafe-inline'", "cdn.jsdelivr.net"],
            "img-src": validMediaSources,
            "media-src": validMediaSources,
          },
        },
      },
    },
    {
      name: "strapi::cors",
      config: {
        enabled: true,
        header: "*",
        origin: ["*"],
      },
    },
    "strapi::poweredBy",
    "strapi::logger",
    "strapi::query",
    "strapi::body",
    "strapi::favicon",
    "strapi::public",
  ];
};

Similarly, on S3, here is my CORS permissions JSON for the uploads bucket:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": []
    }
]

Despite this, when I attempt to download an uploaded piece of media from the media library, I receive a CORS error, e.g. Access to XMLHttpRequest at 'https://powderhouse-strapi-uploads.s3.amazonaws.com/thumbnail_alec_sketch_d9599ceb8a.png?updated_at=2022-02-07T22:32:16.898Z' from origin 'https://api.powderhouse.org' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I asked about this in the Strapi discord, and it seems that the CORS headers are being stripped, perhaps because of Render’s use of Cloudflare proxy services? Note the cf-cache-status value returned from curl -I https://api.powderhouse.org/uploads:

HTTP/2 404 
date: Tue, 08 Feb 2022 20:59:01 GMT
content-type: application/json; charset=utf-8
content-security-policy: script-src 'self' 'unsafe-inline' cdn.jsdelivr.net;img-src 'self' data: cdn.jsdelivr.net strapi.io powderhouse-strapi-uploads.s3.us-east-1.amazonaws.com powderhouse-strapi-uploads.s3.amazonaws.com api-powderhouse-org.onrender.com;media-src 'self' data: cdn.jsdelivr.net strapi.io powderhouse-strapi-uploads.s3.us-east-1.amazonaws.com powderhouse-strapi-uploads.s3.amazonaws.com api-powderhouse-org.onrender.com;connect-src 'self' https:;default-src 'self';base-uri 'self';block-all-mixed-content;font-src 'self' https: data:;frame-ancestors 'self';object-src 'none';script-src-attr 'none';style-src 'self' https: 'unsafe-inline'
expect-ct: max-age=0
referrer-policy: no-referrer
strict-transport-security: max-age=31536000; includeSubDomains
vary: Accept-Encoding
x-content-type-options: nosniff
x-dns-prefetch-control: off
x-download-options: noopen
x-frame-options: SAMEORIGIN
x-permitted-cross-domain-policies: none
x-powered-by: Strapi <strapi.io>
cf-cache-status: MISS
server: cloudflare
cf-ray: 6da7c7882d67187d-EWR
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400

Any help would be appreciated; thank you!

FYI, via Render support, tried changing the origin: ["*"], field of the strapi::cors configuration to origin: "*",. Still no luck.

I can also confirm that this problem does not occur when running Strapi locally.

Following this issue, I also found that disabling the cache seems to resolve the issue. I’m not sure what to make of this.

Just to close the loop here, we have been working with Alec via support ticket to test this.

We have been able to test the scenario with the same s3 and Strapi CORS config, but were not able to reproduce the CORS error when downloading the image. We have tested this on Strapi v4.0.0 and v4.0.2 and with the image provided by Alec as well. At this point, we do not suspect that this is a Render-related issue.

This is a bit of a shot in the dark, but it might be related.

Check out Strapi’s Cloudinary plugin. In its docs, it shows some content security policy changes necessary to use it.

I wonder if something similar has to be done to serve media from S3?