Today I explore the steps required to embed and host a Unity WebGL build within a static Ghost CMS page using gzip compression.


  • A WebGL Build of your game out of Unity using the default gzip compression algorithm
  • A custom theme (or a way to alter your theme)
  • A way to ssh into your server and edit your nginx configuration (so probably a self-hosted solution...)

The Problem

Games are really big, especially by internet standards, where we want to be transmitting as little "across the wire" as possible. Therefore engines such as Unity try to help us out by compressing the archives we need to run the game. This compression means smaller file sizes to download. It can also mean faster load times IF we set up our web server correctly. Otherwise we can't take advantage of the browser downloading the gzipped package while it is also extracting it.

Despite this not being an overly complex problem to solve there are a few Ghost specific stumbling blocks that can make what should be a five minute task take three days. In addition to the Ghost specific problems Unity also does not supply configuration steps for nginx so I can save you some Googling, as I have already done it for you.

The Solution

There are two major steps to getting our WebGL build working. The first is to get it on the site. The second is to get it served properly, let's take this one step at a time.

Embedding the Content

As discussed above I am going to assume you already have a Unity WebGL build ready to go, if you haven't go do that first then come back. (Please come back 🙏)

From here how you put it in your theme is up to you (and how much freedom your theme provider gives you). We can use my side project "The Interview" as an example which uses a custom page template as described in the Ghost Documentation. You will notice I am following the page-slug notation in my theme.

With your page template set up and being correctly routed to you now need to do two things:

  1. Move the Build and TemplateData folders into your asset folders. The exact location will vary depending on how you layout your theme. I keep mine in a Projects sub-folder. While you're in your file explorer head into the Build folder and change your NameOfGame.json file to me you'll need this later.
  2. Open the index.html file generated by Unity and paste the contents into your custom page template in your theme. Feel free to remove any content you don't think will be relevant for your page. I have removed the title and the option to full screen my project.

    The only content you really need here are the link tag for the style sheet, the four script tags to reference the relevant content and the divs that contains the webgl-content class and the unityContainer id.
  3. You need to update the links in your link and script tags accordingly because they will no longer be found in the same location as the Unity build has them sitting right now.
  4. Again we can follow along with the Ghost Documentation and use the asset helper syntax to properly reference the new location of these files (again for me this is under a sub-folder called projects.

    E.g. it might now look like {{asset "js/projects/interview/Build/UnityLoader.js"}}
  5. The last script tag is a little more complicated because we can't seem to use the asset helper from within JavaScript land so I had to create a reference relative to the assets folder.

    E.g. it might now look like var unityInstance = UnityLoader.instantiate("unityContainer", "/assets/js/projects/interview/Build/Interview.txt", {onProgress: UnityProgress});

Now here is something really important to note so I am going to bold the following. When changing the URL for this part to be relative to the assets folder also change the extension to .txt from .json to match the change we made in step 1.

We only need to do the step above because if we do not then we will get a 404 on the file even though its path is valid. It took a lot of Googling and I stumbled across the answer by accident but Ghost blacklists all json files from being served to prevent accidentally leaking configuration. With that in mind and knowing there is nothing sensitive in the file we can change the extension to ignore the blacklist.

With the above complete you should see your files embedded on your local install (which I always recommend you do when testing your themes). If you have, congratulations you are half way there!

Serving the Content Properly

Go ahead and upload your new theme if you haven't already. Check your site and revel in your skills as the Unity WebGL appears before you, really, really slowly. If you open the Developer Console for your given browser (normally F12, but Google it if you are unsure) you're likely to see the warning You can reduce your startup time if you configure your web server to host .unityweb files using gzip compression. This is what we will fix now.

  1. As mentioned up front you will need to be able to go in and edit your nginx install. Depending on how you're self hosting (or who your host is) will depend how that works. If you're unsure hit me up on Twitter.
  2. If you are running a Digital Ocean 1-Click installer like I am you can find the file at /etc/nginx/sites-enabled/your-site.conf. In my particular case (because I run my site over https) I had to edit /etc/nginx/sites-enabled/ Below the .well-known block you can add the following code snippet.
    #Serve .unityweb files properly as being gzipped. You need to set the root to the theme folder or it fails?
    #Location regex from
    location ~ .+\.unityweb$ {
        root /var/www/ghost/content/themes/your-theme-name;
        add_header Content-Encoding gzip;
  3. Be careful to change the last part of your root property to match the name of your theme.
  4. Once the file is saved restart nginx, for me using Ubuntu it's as simple as sudo systemctl restart nginx.
  5. Check out your console after refreshing the page and you should be done.

One quick note here is that the default mime type applied to files that is does not know about on the 1-Click install for Digital Ocean is application/octet-stream which is what we want to serve in this case. If your nginx configuration is different you can either edit the mime.types file to set .unityweb files to be of type application/octet-stream or you can add the header Content-Type in the location block described above if you like.


A few tricky steps but that's mostly just getting down to be familiar with how a Ghost theme is composed and where to look for files. Additionally the 1-Click setup on DO is done to take into account SSL an other factors which is why the default advice to add the location block by itself doesn't work without the added root property.

In future if you want to showcase more of your work you will only need to copy across the appropriate assets and make a new page. The nginx configuration will handle all .unityweb files site-wide as long as your theme name remains unchanged. If you do change the name of your theme you might want to go update nginx root just to take advantage of the gzip decompression.