Migration guide · reviewed 22 Aug 2026
From CGI to PSGI without changing behaviour.
Moving a script into a persistent process changes more than startup time. Freeze the HTTP contract first, then make process lifetime, cleanup and deployment explicit.
01
Inventory the contract, not the files
A CGI script may read query parameters, form bodies, cookies and server variables, then print headers and a body. The maintenance contract is the observable result: status, headers, body, redirects and side effects for a representative request set.
02
Build a characterisation seam
Choose ordinary and hostile inputs: missing parameters, repeated parameters, wrong methods, malformed bodies, oversized values and failed writes. Record only behaviour you intend to preserve. If the old response reflects unsanitised input or exposes a file path, the test should describe a defect to remove, not a contract to keep.
- Request
- Method, path, query, selected headers and a redacted body fixture.
- Response
- Status, ordered redirects, security-relevant headers and stable body assertions.
- Side effect
- Files, mail, database writes and external calls, each behind a replaceable boundary.
- Failure
- What the user sees, what is logged and whether retrying can duplicate work.
03
Audit process-lifetime assumptions
Classic CGI normally starts a process for a request and then exits. A PSGI application can remain loaded and serve later requests. That makes hidden globals, cached user data, changed working directories, stale filehandles and incomplete cleanup observable across requests. The PSGI FAQ calls this out directly: request cleanup that happened accidentally at process exit becomes application work.
- Reset request-specific state explicitly.
- Open resources with scoped lifetime and close them on every path.
- Do not store user or request data in package variables.
- Assume equivalent application objects may run in multiple processes; a memory flag is not a lock.
04
Choose the smallest migration step
There is no single correct adapter. An application already built on CGI::Application can expose its PSGI coderef. A largely untouched CGI program can be contained temporarily with CGI::Compile and CGI::Emulate::PSGI. New request-handling code can return the PSGI response shape directly or use a framework that supports PSGI.
05
Cut over with two proofs
- Run the same redacted request fixtures against the old and candidate paths.
- Compare status, redirects, headers, stable body fields and side effects.
- Run consecutive requests in one process to expose leaked state.
- Exercise concurrent requests where shared files or records are written.
- Deploy behind a reversible route or upstream switch.
- Watch errors, latency and side-effect counts before removing the old path.
06
Primary sources
- PSGI specification — application environment and response contract.
- Plack documentation — handlers, middleware,
.psgiconvention and test tooling. - PSGI FAQ — migration choices and persistent-process cleanup warning.
- CGI::Alternatives — maintained overview of current Perl web options.
Sources checked 22 August 2026. This page describes a migration procedure; it does not claim that a specific application has passed it.