Repository: LemmyNet/lemmy (language: rust)

Issue:
Allow multiple values as cors_origin
> I mentioned this in #5185 already, I don't think it makes much sense to only allow a single value for CORS-allowed domains. For example, if you'd want to allow people to use the official photon deployment at https://phtn.app/ and also the official voyager deployment at https://vger.app/ but no other domains you'll need to allow both of them. The current implementation does not allow for that.

_Originally posted by @Nothing4You in https://github.com/LemmyNet/lemmy/issues/5194#issuecomment-2471508061_
            


Discussion / hints:

Fix patch:
diff --git a/config/defaults.hjson b/config/defaults.hjson
index b5d3b1004d..4922d74aad 100644
--- a/config/defaults.hjson
+++ b/config/defaults.hjson
@@ -110,7 +110,11 @@
     bind: "127.0.0.1"
     port: 10002
   }
-  # Sets a response Access-Control-Allow-Origin CORS header
+  # Sets a response Access-Control-Allow-Origin CORS header. Can also be set via environment:
+  # `LEMMY_CORS_ORIGIN=example.org,site.com`
   # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
-  cors_origin: "lemmy.tld"
+  cors_origin: [
+    "lemmy.tld"
+    /* ... */
+  ]
 }
diff --git a/crates/routes/src/utils/mod.rs b/crates/routes/src/utils/mod.rs
index ccb363df95..632905c902 100644
--- a/crates/routes/src/utils/mod.rs
+++ b/crates/routes/src/utils/mod.rs
@@ -9,30 +9,19 @@ pub fn cors_config(settings: &Settings) -> Cors {
   let self_origin = settings.get_protocol_and_hostname();
   let cors_origin_setting = settings.cors_origin();
 
-  // A default setting for either wildcard, or None
-  let cors_default = Cors::default()
-    .allow_any_origin()
+  let mut cors = Cors::default()
     .allow_any_method()
     .allow_any_header()
     .expose_any_header()
     .max_age(3600);
 
-  match (cors_origin_setting.clone(), cfg!(debug_assertions)) {
-    (Some(origin), false) => {
-      // Need to call send_wildcard() explicitly, passing this into allowed_origin() results in
-      // error
-      if origin == "*" {
-        cors_default
-      } else {
-        Cors::default()
-          .allowed_origin(&origin)
-          .allowed_origin(&self_origin)
-          .allow_any_method()
-          .allow_any_header()
-          .expose_any_header()
-          .max_age(3600)
-      }
+  if cfg!(debug_assertions) || cors_origin_setting.contains(&"*".to_string()) {
+    cors = cors.allow_any_origin();
+  } else {
+    cors = cors.allowed_origin(&self_origin);
+    for c in cors_origin_setting {
+      cors = cors.allowed_origin(&c);
     }
-    _ => cors_default,
   }
+  cors
 }
diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs
index effd68a640..4577f4e609 100644
--- a/crates/utils/src/settings/structs.rs
+++ b/crates/utils/src/settings/structs.rs
@@ -44,17 +44,19 @@ pub struct Settings {
   // Prometheus configuration.
   #[doku(example = "Some(Default::default())")]
   pub prometheus: Option<PrometheusConfig>,
-  /// Sets a response Access-Control-Allow-Origin CORS header
+  /// Sets a response Access-Control-Allow-Origin CORS header. Can also be set via environment:
+  /// `LEMMY_CORS_ORIGIN=example.org,site.com`
   /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
   #[doku(example = "lemmy.tld")]
-  cors_origin: Option<String>,
+  cors_origin: Vec<String>,
 }
 
 impl Settings {
-  pub fn cors_origin(&self) -> Option<String> {
+  pub fn cors_origin(&self) -> Vec<String> {
     env::var("LEMMY_CORS_ORIGIN")
       .ok()
-      .or(self.cors_origin.clone())
+      .map(|e| e.split(',').map(ToString::to_string).collect())
+      .unwrap_or(self.cors_origin.clone())
   }
 }
 


Test patch: