How to Hijack your head (HTML head tag)

Disclosure

This is aimed as a guide to add scripts or styles to the <head> tag of websites that you host and do not have access to the source or do not want to monkey patch source.

Additional note, you can ride on the free tier of CloudFlares service workers if you do not serve more than 100k requests on your foundry instance /join route.

Introduction

In this example, we will be creating a service worker on CloudFlare which gives us the option to inject a <link> element into the <head> element of a websites served HTML. For the example, we will be inject a style sheet into the <head> to change the appearance of FoundryVTT.

The link we will be applying is the style effects from Foundry VTT Prettier Login Screen by TheEpicSnowWolf.

Requirements

  • CloudFlare domain, or sub-domain, pointing to your FoundryVTT instance.
  • A hosted FoundryVTT instance that can be served by your domain or sub-domain.
  • Minimal knowledge of JavaScript.

Steps

1). Open CloudFlare and Login.

2). (If you have multiple domains) Use the drop down or the cards to select the domain your foundry instance is hosted on.

3). Click the Workers tab at the top:

4). On the Workers page, click Manage Workers

5). On the Manage Workers page, click Create a Worker

6). In the script section, lets create an element handler:

class ElementHandler {
  element(element) {
    element.append(`<link
      rel="stylesheet" 
      type="text/css" 
      data-id="foundry-login"
      href="https://cdn.jsdelivr.net/gh/TheEpicSnowWolf/Foundry-VTT-Prettier-Login-Screen@main/foundry_login.css"  
    >`, {html: true});
    console.log("injected");
  }
}

This element handler, will consume the <head> element on the HTML dom and append a link to the foundry_login.css script found in the Foundry VTT Prettier Login Screen repository by TheEpicSnowWolf.

7). Next lets add a handle request function:

async function handleRequest(req) {
  const res = await fetch(req)

  if (res.url.includes('example.com/join')) {
    return new HTMLRewriter().on("head", new ElementHandler()).transform(res)
  }  

  return res;
}

This function is designed to take the request, process the request, and check if the URL itself matches the /join route. In the script above, replace the example.com with your domain or sub-domain.domian.tld (ex dnd.example.com)

8). After that, add the event listener to hook into the worker API:

addEventListener("fetch", (event) => {
  event.respondWith(
    handleRequest(event.request).catch(
      (err) => new Response(err.stack, { status: 500 })
    )
  );
});

9). Click the Save and Deploy button:

10). With the script saved, click the script name in the upper right hand corner:

11). On the workers configuration page, uncheck the box that says Workers.dev route

12). Use the drop down menu in the upper right hand corner to select your domain name.

13). Click the Workers tab again

14). This time, click the Add Route button.

15). In the Add Route screen, change the Route to be example.com or sub-domain.domian.tld with a trailing /join if you are doing this for foundry.

16). In the Worker drop down, select the new worker you created:

17). Click the save button:

18). Finally, visit your website where the service worker is attached ❤️

Conclusion

This is incredibly useful for sites where you do not want to monkey with the code and instead inject other scripts. It can be used for Analytics, Authentication, and much more. The possibilities are endless! 😄

Source Code

Source code for service worker: Foundry VTT Service Worker

Shoutouts

27