# Full URL in assets erb file --\> multisite issues

**URL:** https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393
**Category:** Bug
**Created:** [July 30, 2019, 11:53am UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393 "2019-07-30T11:53:37Z")
**Posts on this page:** 18
**Page:** 1

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [July 30, 2019, 11:53am UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/1 "2019-07-30T11:53:37Z")

</div>

The new workbox service worker implementation uses `UrlHelper.absolute`. Since this is a compiled asset, it stores the full URL, containing the full hostname of the primary host in a multisite environment.

[https://github.com/discourse/discourse/blob/master/app/assets/javascripts/service-worker.js.erb#L3-L6](https://github.com/discourse/discourse/blob/master/app/assets/javascripts/service-worker.js.erb#L3-L6)

I think it should be using `UrlHelper.local_cdn_url` instead. This also circumvents the need to recompile assets after changing the hostname.

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [July 30, 2019, 3:27pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/2 "2019-07-30T15:27:57Z")

</div>

> [@RGJ](#):
>
> I think it should be using `UrlHelper.local_cdn_url` instead

I think the general consensus is that Service Workers shouldn’t ever be served from a CDN:

> <https://github.com/w3c/ServiceWorker/issues/940>
>
> Hey all,
> 
> I want to serve a service worker from a CDN, but I can't figure out ho…w to get that to work.
> 
> I expected this to work:
> 
> \`\`\`
> // The header \`Service-Worker-Allowed: www.example.com\` is set in the response
> var swURL = "cdn.example.com/sw.js";
> var options = {scope: 'www.example.com'};
> navigator.serviceWorker.register(swURL, options);
> \`\`\`
> 
> But it throws the following error in Chrome:
> 
> \`\`\`
> Uncaught (in promise) DOMException: Failed to register a ServiceWorker:
> The origin of the provided scriptURL ('https://cdn.example.com/sw.js')
> does not match the current origin ('https://www.example.com').
> \`\`\`
> 
> With the current wording, it seems like Service-Worker-Allowed allows loading the service worker from a remote origin.
> 
> \> \`Service-Worker-Allowed\`
> \> Indicates the user agent will override the path restriction, which limits the maximum allowed scope url that the script can control, to the given value.
> \> The value is a URL. If a relative URL is given, it is parsed against the script’s URL.
> 
> https://slightlyoff.github.io/ServiceWorker/spec/service\_worker/#service-worker-allowed
> 
> However, all of the examples and discussion only use Service-Worker-Allowed with relative URLs.
> 
> Digging through the Chromium code, I'm pretty sure the error is thrown before the request is made to the url, which means it's made before we can check the header on the sw response:
> 
> \`\`\`
> void ServiceWorkerContainer::registerServiceWorkerImpl(/\* ... \*/)
> {
> // ...
> if (!documentOrigin-\>canRequest(scriptURL)) {
> RefPtr\<SecurityOrigin\> scriptOrigin = SecurityOrigin::create(scriptURL);
> callbacks-\>onError(WebServiceWorkerError(WebServiceWorkerError::ErrorTypeSecurity, String("Failed to register a ServiceWorker: The origin of the provided scriptURL ('" + scriptOrigin-\>toString() + "') does not match the current origin ('" + documentOrigin-\>toString() + "').")));
> return;
> }
> // ...
> m\_provider-\>registerServiceWorker(patternURL, scriptURL, callbacks.release());
> }
> \`\`\`
> 
> https://cs.chromium.org/chromium/src/third\_party/WebKit/Source/modules/serviceworkers/ServiceWorkerContainer.cpp?q=%22does+not+match+the+current+origin%22&sq=package:chromium&dr=C&l=224
> 
> You can follow the rabbit hole down 'canRequest', but I'm pretty sure nothing in that function allows you to allow remote scripts dynamically (e.g. with a header).
> 
> Questions:
> \- Is it possible to serve a service worker from a remote origin through some other means?
> \- The spec is currently ambiguous about remote origins -- it doesn't say they aren't allowed, but it doesn't say they are either. Should Service-Worker-Allowed let you serve from a remote origin?
> 
> Thanks for your time! BTW, you all have done excellent work with the spec.

> <https://stackoverflow.com/questions/34489742/hosting-service-worker-file-from-cdn-google-drive/34515151#34515151>

> [@RGJ](#):
>
> The new workbox service worker implementation uses `UrlHelper.absolute` . Since this is a compiled asset, it stores the full URL, containing the full hostname of the primary host in a multisite environment.

Oh so you mean the `importScripts` files? Do you have a multi-site cluster where every site has a different CDN URL ?

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [July 30, 2019, 4:18pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/3 "2019-07-30T16:18:12Z")

</div>

Yes, `importScripts` and `modulePathPrefix`, on lines 3 and 6 of that ERB file.

But you are not understanding me completely. The issues are happening where there is _no_ CDN on a multisite cluster.

Both `UrlHelper.absolute` and `UrlHelper.local_cdn_url` can handle situations with and without CDN.

`absolute`:

no cdn: `https://primarysite.ofmultisitecluster.com/javascripts/workbox/workbox-sw.js` \*\*bad - remote origin for all sites but the primary, exposing primary cluster hostname \*\*  
cdn: `//cdnurl/javascripts/workbox/workbox-sw.js`

`local_cdn_url:`

no cdn:` /javascripts/workbox/workbox-sw.js` **good - relative url**  
cdn: `//cdnurl/javascripts/workbox/workbox-sw.js`

so the latter is what we want.

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [July 30, 2019, 4:52pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/4 "2019-07-30T16:52:55Z")

</div>

> [@RGJ](#):
>
> If service workers shouldn’t be served from a CDN ever then the fix is much easier and both lines should just have the relative url instead of a call to a `UrlHelper` function, since you only need the /service-worker part.

The problem is that those lines (3 ad 6) are not about the service worker file.

The service worker comes from the base domain (as it can’t be served from a CDN) but this service worker lazily imports scripts at runtime (in this case the workbox library files) and those can be served from a CDN.

So the problem is that your multisite cluster doesn’t have a CDN configured so that is what exposes this bug, which is masked when `DISCOURSE_CDN` is set. Just wanted to know why it doesn’t affect us.

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [July 30, 2019, 5:06pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/5 "2019-07-30T17:06:44Z")

</div>

You are correct, I’ve edited my second post to correct my mistakes.  
It’s not about the service worker file, it’s IN the service worker file.

> [@Falco](#):
>
> So the problem is that your multisite cluster doesn’t have a CDN configured so that is what exposes this bug, which is masked when `DISCOURSE_CDN` is set. Just wanted to know why it doesn’t affect us.

Yes, that is exactly when the bug is being exposed.

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [August 1, 2019, 2:55pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/6 "2019-08-01T14:55:56Z")

</div>

Just checking up - is my diagnosis correct and this this a bug that is going to be fixed? Is there anything you need from us that would help?

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [August 1, 2019, 5:06pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/7 "2019-08-01T17:06:35Z")

</div>

Yes, it looks a bug that needs fixing. I will get to it this week!

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [August 2, 2019, 7:03pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/9 "2019-08-02T19:03:11Z")

</div>

> [@RGJ](#):
>
> I think it should be using `UrlHelper.local_cdn_url` instead. This also circumvents the need to recompile assets after changing the hostname.

Hmmm I just tried it out on a Meta console:

```ruby
## Current
[1] pry(main)> UrlHelper.absolute("/javascripts/workbox/workbox-sw.js")
=> "https://d3bpeqsaub0i6y.cloudfront.net/javascripts/workbox/workbox-sw.js"

### Proposed change
[2] pry(main)> UrlHelper.local_cdn_url("/javascripts/workbox/workbox-sw.js")
=> "/javascripts/workbox/workbox-sw.js"

```

Those doesn’t look interchangeable functions to me.

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [August 2, 2019, 7:37pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/10 "2019-08-02T19:37:45Z")

</div>

You are correct, `local_cdn_url` replaces a local URL with a CDN URL. And this is not even a local URL but a relative one.

So I think these would suffice instead of those `UrlHelper` calls?

`importScripts("<%= (Discourse.asset_host || '') + "/javascripts/workbox/workbox-sw.js" %>");`

and

` modulePathPrefix: (Discourse.asset_host || '') + "/javascripts/workbox",`

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [August 2, 2019, 7:57pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/11 "2019-08-02T19:57:22Z")

</div>

Reading the current implementation of `UrlHelper.absolute`:

> <https://github.com/discourse/discourse/blob/main/lib/url_helper.rb#L28-L31>

Looks like it will compose the URL by concatenating `Discourse.base_url_no_prefix` plus the parameter when CDN is `nil`, which is your case.

So the problem is that `Discourse.base_url_no_prefix` is returning always the first host in the multisite environment?

 ![image](https://global.discourse-cdn.com/meta/original/3X/b/7/b7505aa967de485adea3c6f70badc7f1b5c80598.jpeg)

Looking into the code 👀

> <https://github.com/discourse/discourse/blob/main/lib/discourse.rb#L286-L293>

the name of the variable here `current_hostname` @ 288 **strongly** suggests something multisite aware 🤔

and by

> <https://github.com/discourse/discourse/blob/main/lib/discourse.rb#L273-L276>

it looks like it is. Dead end so far…

Looking elsewhere, this route gained some special sauce because browsers love to hammer it HARD, and we aren’t allowed to put it on a CDN and make it someone else problem. While doing this, we had a bug involving a multisite leak, which was fixed by @sam one year ago:

[https://github.com/discourse/discourse/commit/abf0b1c5bd8afdfdc53a035eccac3485de659bfa](https://github.com/discourse/discourse/commit/abf0b1c5bd8afdfdc53a035eccac3485de659bfa)

Is there a possibility that the way you are serving this multisite cluster is caching this route in a leaky way, like we were in early 2018?

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [August 2, 2019, 8:53pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/12 "2019-08-02T20:53:44Z")

</div>

> [@Falco](#):
>
> So the problem is that `Discourse.base_url_no_prefix` is returning always the first host in the multisite environment?

No, the problem is that it does that when precompiling assets, and that becomes an issue on multisite.  
So the solution is to not include the hostname in the assets ever (except for an asset CDN if it happens to be set since that is always shared between the multisite hosts anyway)

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [August 4, 2019, 6:07pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/13 "2019-08-04T18:07:48Z")

</div>

@falco did you see my [proposed solution two posts above this one](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/10)?

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [August 4, 2019, 7:21pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/14 "2019-08-04T19:21:21Z")

</div>

Yes, but in my tests it doesn’t cover subfolders without a CDN 😭

I think we will need to use:

```ruby
"#{Discourse.asset_host}#{Discourse.base_prefix}/javascripts/workbox"

```

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [August 4, 2019, 8:49pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/15 "2019-08-04T20:49:34Z")

</div>

Hmm good point about the subfolder.

But…`Discourse.asset_host` can be nil and I’ve never heard of `Discourse.base_prefix` ?

How about this:

`importScripts("<%= (Discourse.asset_host || GlobalSetting.relative_url_root) + "/javascripts/workbox/workbox-sw.js" %>");`

`modulePathPrefix: "<%= (Discourse.asset_host || GlobalSetting.relative_url_root) + "/javascripts/workbox" %>",`

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [August 5, 2019, 7:36pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/16 "2019-08-05T19:36:37Z")

</div>

> [@RGJ](#):
>
> But… `Discourse.asset_host` can be nil

That is exactly what we want in this case:

```ruby
irb(main):001:0> puts "a#{nil}bc"
abc

```

> [@RGJ](#):
>
> I’ve never heard of `Discourse.base_prefix` ?

Oh I meant `Discourse.base_path`.

Just commited a fix, please check it out.

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [August 5, 2019, 8:15pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/17 "2019-08-05T20:15:18Z")

</div>

Looks good for our case.. thanks!

But… just to make sure.. I’m not sure how you guys are handling assets on a CDN in combination with a subfolder, but if you use `Discourse.asset_host`, do you still prefix all paths on the asset host with the subfolder path _as well_? Because that is what the code is doing now.  
If that _is_ what you’re doing then you can completely ignore this paragraph 🙂

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [August 5, 2019, 8:20pm UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/18 "2019-08-05T20:20:44Z")

</div>

This whole subfolder + CDN thing caused use plenty of problems indeed. @featheredtoast did some work streamlining this and we spent a lot of time ensuring all our asset serving code can properly work on those weird combinations of buckets, subfolders, etc.

I _think_ we are safe 😄, but if needed we can reopen this.

Thanks for the bug report!

---

<div class="post-metadata">

### Author: ![codinghorror](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/codinghorror/32/110067_2.png) [@codinghorror](https://meta.discourse.org/u/codinghorror)
#### Post date: [August 13, 2019, 1:00am UTC](https://meta.discourse.org/t/full-url-in-assets-erb-file-multisite-issues/124393/19 "2019-08-13T01:00:06Z")

</div>

This topic was automatically closed after 7 days. New replies are no longer allowed.
