diff --git a/doc/doc.md b/doc/doc.md
index 1174666c9175f0dcb1f88cf761eb76f915060426..fbc41934ea4ee6e42094ba97d793b201af78beb6 100644
--- a/doc/doc.md
+++ b/doc/doc.md
@@ -1278,6 +1278,9 @@ This needs to be changed if the output format is Markdown, for instance.
   - `user_keywords` A list of keywords that will be marked in "prettified" code. Useful if
 you want to display your own functions in a special way. Each keyword may be styled differently
 (using CSS). Only works when `pretty` is set to 'lua' (the default).
+  - `postprocess_html` function that allows a last-minute modification to the produced HTML page.
+The arguments are the raw HTML that's intended to be written out (a string), and the module object.
+The string this function returns will be what's actually gets written out.
 
 _Available functions are:_
 
diff --git a/ldoc.lua b/ldoc.lua
index 80672eea8d20ad2287fa6a897360583f7e20f1dd..0c49b9dad82efc45845f4176dd9c2793c733ad81 100644
--- a/ldoc.lua
+++ b/ldoc.lua
@@ -234,6 +234,7 @@ local ldoc_contents = {
    'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles',
    'unqualified', 'custom_display_name_handler', 'kind_names', 'custom_references',
    'dont_escape_underscore','global_lookup','prettify_files','convert_opt', 'user_keywords',
+   'postprocess_html',
 }
 ldoc_contents = tablex.makeset(ldoc_contents)
 
diff --git a/ldoc/html.lua b/ldoc/html.lua
index 067c10345b391b584d3121d7d49bf2692d43cd07..9e135da00ccadfe8417cd2fe6bab7656f92c80fe 100644
--- a/ldoc/html.lua
+++ b/ldoc/html.lua
@@ -278,6 +278,24 @@ function ldoc.source_ref (fun)
       quit("template not found at '"..args.template.."' Use -l to specify directory containing ldoc.ltp")
    end
 
+   -- Runs a template on a module to generate HTML page.
+   local function templatize(template_str, ldoc, module)
+      local out, err = template.substitute(template_str, {
+         ldoc = ldoc,
+         module = module,
+         _escape = ldoc.template_escape
+      })
+      if not out then
+         quit(("template failed for %s: %s"):format(
+               module and module.name or ldoc.output or "index",
+               err))
+      end
+      if ldoc.postprocess_html then
+         out = ldoc.postprocess_html(out, module)
+      end
+      return cleanup_whitespaces(out)
+   end
+
    local css = ldoc.css
    ldoc.output = args.output
    ldoc.ipairs = ipairs
@@ -299,13 +317,8 @@ function ldoc.source_ref (fun)
       save_and_set_ldoc(ldoc.module.tags.set)
    end
    set_charset(ldoc)
-   local out,err = template.substitute(module_template,{
-      ldoc = ldoc,
-      module = ldoc.module,
-      _escape = ldoc.template_escape
-    })
+   local out = templatize(module_template, ldoc, ldoc.module)
    ldoc.root = false
-   if not out then quit("template failed: "..err) end
    restore_ldoc()
 
    check_directory(args.dir) -- make sure output directory is ok
@@ -352,17 +365,8 @@ function ldoc.source_ref (fun)
          if ldoc.body and m.postprocess then
             ldoc.body = m.postprocess(ldoc.body)
          end
-         out,err = template.substitute(module_template,{
-            module=m,
-            ldoc = ldoc,
-            _escape = ldoc.template_escape
-         })
-         if not out then
-            quit('template failed for '..m.name..': '..err)
-         else
-            out = cleanup_whitespaces(out)
-            writefile(args.dir..lkind..'/'..m.name..args.ext,out)
-         end
+         local out = templatize(module_template, ldoc, m)
+         writefile(args.dir..lkind..'/'..m.name..args.ext,out)
          restore_ldoc()
       end
    end