Convert POD to HTML Reliably with pod2html
You will finish with a repeatable command that converts a Perl POD document into an HTML file, keeps temporary cache files out of your project, and gives you a quick way to check the result. The examples use pod2html from Perl 5.38.2, supplied by package version 5.38.2-3.2ubuntu0.6 on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a shell, a readable .pod file and permission to write the chosen output directory. This guide changes only the output file and a cache directory. It does not install software, alter Perl modules or publish the generated HTML.
1. Check the installed command
Confirm which executable will run and inspect its built-in usage text. These are ordinary, read-only commands and do not require elevated privileges:
$ command -v pod2html
/usr/bin/pod2html
$ dpkg-query -W -f='${Package} ${Version}\n' perl
perl 5.38.2-3.2ubuntu0.6
$ pod2html --help
Usage: /usr/bin/pod2html --help --htmldir=<name> --htmlroot=<URL>
--infile=<name> --outfile=<name> ...
The installed command accepts long options such as --infile and --outfile. It does not provide a useful --version option, so use the package query when you need to record the version. The manpage shipped here is dated 2026-09-14 and identifies the underlying Perl release as 5.38.2.
Checkpoint
Stop here if command -v finds a different executable than the one you intended. PATH order can otherwise make a conversion look different from a test run.
2. Choose separate input, output and cache paths
For a concrete example, replace the placeholders with paths that already exist or that you are allowed to create:
INPUT='/path/to/project/lib/Example.pod'
OUTPUT='/path/to/project/build/Example.html'
CACHE='/tmp/pod2html-cache'
mkdir -p "$(dirname "$OUTPUT")" "$CACHE"
test -r "$INPUT"
The final command will read INPUT and create or replace OUTPUT. The mkdir command is ordinary user work when those directories belong to you. test produces no output when the input is readable; a non-zero status means you should fix the path or its permissions before converting.
Warning
--outfile writes the named file. Check the path before running it, especially when a shell variable came from another script. If you replace an existing file, recover it from version control or your normal backup rather than assuming pod2html can undo the change.
Without --cachedir, pod2html uses the current working directory for its cache. Set it explicitly so a conversion does not leave a cache file beside your source or overwrite a similarly named file in an unexpected directory. The cache is implementation data, not the HTML output.
3. Run a predictable conversion
Use explicit input and output paths, a dedicated cache, and the quiet, no-extra-content choices below:
pod2html \
--infile="$INPUT" \
--outfile="$OUTPUT" \
--cachedir="$CACHE" \
--noindex \
--noheader \
--nopoderrors \
--quiet
A successful run normally prints nothing because of --quiet and returns status 0. It creates the HTML at OUTPUT. --noindex omits the generated index, --noheader leaves out the NAME-based header and footer, and --nopoderrors omits a POD ERRORS section. These options make the result easier to embed in a larger documentation site, but they also hide useful diagnostics. During a first conversion, leave out --quiet and --nopoderrors if you need to investigate warnings.
Checkpoint
Confirm that the output exists and is not empty:
test -s "$OUTPUT" && printf 'created: %s\n' "$OUTPUT"
sed -n '1,35p' "$OUTPUT"
The installed converter writes an XHTML 1.0 document with a doctype, html, head and body elements. Expect headings and paragraphs derived from the POD. Do not mistake a zero exit status for a semantic review: malformed or surprising POD still deserves inspection.
4. Decide how cross-references should work
Cross-reference behaviour is the option most likely to be surprising. Use --htmldir when the generated files should link to one another with relative paths. Use --htmlroot when links should be rooted at a base URL. The two options are mutually exclusive.
pod2html \
--infile="$INPUT" \
--outfile="$OUTPUT" \
--cachedir="$CACHE" \
--htmldir='/path/to/project/build' \
--noheader \
--nopoderrors
Do not pass --htmldir and --htmlroot together. If the published site is served below a known URL, the alternative looks like this:
pod2html \
--infile="$INPUT" \
--outfile="$OUTPUT" \
--cachedir="$CACHE" \
--htmlroot='https://docs.example.invalid/perl' \
--noheader \
--nopoderrors
Use a real host name in production, not the .invalid placeholder. Keep the base URL aligned with the location where the HTML will actually be served. A wrong root can produce links that look plausible but lead readers to the wrong documentation tree.
5. Choose presentation options deliberately
The default conversion generates an index at the top of the page. Add --index explicitly when that is wanted, or use --noindex for a small page or a site whose navigation is supplied elsewhere. The default is no header and footer; use --header when you want blocks containing the POD NAME section.
To add a stylesheet link, pass a URL with --css:
pod2html \
--infile="$INPUT" \
--outfile="$OUTPUT" \
--cachedir="$CACHE" \
--css='/css/perl-docs.css' \
--noheader \
--nopoderrors
pod2html does not copy that stylesheet. The URL must resolve from the generated document's published location. Treat --backlink similarly: it makes =head1 directives link back to the top of the HTML file, while the default --nobacklink does not.
6. Handle warnings and the cache
When a conversion reports POD errors, first rerun it without --quiet and with the default error section enabled. That keeps the diagnostic visible in the output while you fix the source:
pod2html \
--infile="$INPUT" \
--outfile="$OUTPUT" \
--cachedir="$CACHE"
grep -n -A4 'POD ERRORS' "$OUTPUT"
Only use --nopoderrors after you have decided that warnings should not appear in the published page. Suppressing the section does not repair invalid POD.
Use --flush when the directory cache is stale and you want pod2html to rebuild it. This affects cache data, not the source POD, but it can make a large documentation conversion slower:
pod2html \
--flush \
--cachedir="$CACHE" \
--infile="$INPUT" \
--outfile="$OUTPUT" \
--noheader
If the output does not change after editing a linked POD file, check the cache location and consider a deliberate flush. Do not delete an entire project directory to clear it. Remove only a cache directory that you created for this conversion, after confirming its path:
case "$CACHE" in
/tmp/pod2html-cache) rm -rf -- "$CACHE" ;;
*) printf 'refusing to remove unexpected cache: %s\n' "$CACHE" >&2; exit 1 ;;
esac
This is the one destructive example in the guide. It removes generated cache data only; it does not remove the input or output. If you do not need to reclaim temporary space, leave the cache in place.
7. Verify the finished file
Check the file type, inspect its structure, and look for the content you expect:
file "$OUTPUT"
grep -nE '<!DOCTYPE|<title>|<h1|<p>' "$OUTPUT" | head -20
grep -n 'POD ERRORS' "$OUTPUT" || true
The first command should identify an HTML or XHTML document. The second should show the document title, at least one heading and paragraphs. The final command should print nothing when no POD ERRORS section exists. If a browser displays raw markup, inspect the file for an incomplete write or a wrapper that is serving it as plain text.
For a conversion used in automation, make the exit status and output check part of the same script. Keep the input, output and cache variables quoted, and do not place untrusted option text into the command line. Elevated privileges are not normally needed; use them only when the chosen input or output paths are intentionally protected, and review the resulting ownership before a web server reads the file.
Done means
- The installed
pod2htmland Perl package version were checked. - The input, output and cache paths were chosen explicitly and quoted.
- The generated file is non-empty HTML, with the intended index, header and stylesheet choices.
- Cross-reference mode uses either
--htmldiror--htmlroot, never both. - POD warnings were inspected before any error section was suppressed.
- Only a known, disposable cache directory is removed, if cleanup is needed.