#15213·hugo

Improve the workers/edge middleware support

Author: bepCreated Aug 16, 2026Updated Sep 10, 2026
LabelsProposal

I have been setting up a Google Analytics server proxy[^location] using Cloudflare workers, and I was pleasantly surprised about the smooth sailing.

My setup in Hugo for this outlined:

assets/workers/index.js

import analytics from './analytics.js';

export default {
	async fetch(request, env, ctx) {
		return (await analytics(request, env, ctx)) ?? env.ASSETS.fetch(request);
	},
};

_layouts/partials/worker.html (loaded once with partialCached)

{{ $opts := dict
	"targetPath" "_worker.js"
	"format" "esm"
	"minify" (not hugo.IsDevelopment)
	"params" (dict "measurementID" site.Params.gaMeasurementID)
}}
{{ (resources.Get "workers/index.js" | js.Build $opts).Publish }}
{{ (resources.FromString ".assetsignore" "_worker.js\n").Publish }}

wrangler.toml

name               = 'myapp'
main               = './public/_worker.js'
compatibility_date = '2025-07-31'

[build]
	command = './build.sh'

[assets]
	directory          = './public'
	binding            = 'ASSETS'
	not_found_handling = '404-page'

The above is pretty nice, but there's some warts:

  • Having the script that generates the worker script(s) in the middle of a rendering template (even if effectively run once with partialCached) isn't great.
  • Also, writing these server functions to /public isn't great. It works out for Cloudflare Workers, but for e.g. Netlify you would need to do e.g. hugo && mv public/_edge/analytics.js .netlify/edge-functions/analytics.js.
  • There's no simple to compose handlers pulled in from modules.

[^location]: Recent updates to the protocol, most notably the user_location attribute, has made this a much more viable option. My take on this should be privacy/GPDR-friendly; no cookies and no localStorage usage. I will try to wrap this up in a Hugo Module once I get this fully tested.