Skip to content

Response page architecture

Per-page reference for what PAN-OS actually does with an imported response page: the shape of the file it expects, the tokens and variables available, and the limits it enforces.

Palo Alto documents the existence of these pages and a handful of customization variables. It does not document the file's structure, the substitution semantics, or the import ceiling. Almost everything here was measured against a live firewall, so each claim carries an evidence marker.

Pages

Page Import object Serves
GlobalProtect Portal Login Page global-protect-portal-custom-login-page login.esp, getsoftwarepage.esp
GlobalProtect Portal Home Page global-protect-portal-custom-home-page logout.esp, portal home page
URL Filtering and block pages eleven separate objects dataplane injection into user traffic

The two GlobalProtect pages are the ones this reference exists for — they are the pages with undocumented structure. The block pages are comparatively simple and are covered in one file, per family, because their only per-page difference is which substitution tokens they accept.

Evidence markers

Every non-obvious claim in these files is marked. Treat them as load-bearing:

Marker Meaning
[verified] Observed directly against a live firewall. The observation is recorded alongside it.
[documented] Stated in Palo Alto documentation, with the URL.
[inferred] Reasoned from evidence but not directly tested. May be wrong.
[unverified] Assumed, carried forward, never checked. Treat as a lead, not a fact.

Where a claim was wrong earlier and later corrected, the correction says so. Knowing which beliefs have already failed is worth more than a clean-looking document.

Constraints that apply to both GlobalProtect pages

Import size ceiling: 16,170 bytes [verified]

The limit is enforced on the base64-encoded form of the file, not the file itself. A 24,000-byte import was refused with:

page can be at most 21845 characters, but current length: 32422

32,422 is exactly len(base64.encodebytes(24_000 bytes)) — standard base64 wrapped at 76 columns with a trailing newline. Working back from the 21,845-character cap:

Raw bytes Encoded chars Result
16,000 21,617 accepted [verified]
16,170 21,844 largest that fits [inferred]
16,171 21,848 over [inferred]
20,000 27,019 refused [inferred]
24,000 32,422 refused [verified]

Both endpoints were tested; the exact boundary is arithmetic from the cap PAN-OS quoted. 21,845 is floor(65535 / 3), which reads like a 64 KB field cap divided by three rather than by the 4/3 base64 actually expands at.

This failure is loud. The import is refused with the message above. That makes it the only failure mode in this document that tells you what went wrong.

Do not borrow MAX_BYTES = 17999 from validate.py here — that is a serving-time limit for a different page class. See URL Filtering and block pages.

Content-Security-Policy [verified]

Served on portal responses:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
                         img-src * data:; style-src 'self' 'unsafe-inline';

Read directly, this permits more than expected:

Directive Consequence
script-src 'self' 'unsafe-inline' Inline <script> allowed. External scripts same-origin only.
style-src 'self' 'unsafe-inline' Inline <style> allowed. External stylesheets same-origin only.
img-src * data: Images from any origin, and data: URIs.
default-src 'self' Everything else same-origin. No font-src, so external webfonts are blocked.

This corrects an earlier belief that the CSP blocked external assets outright. It blocks external stylesheets and scripts; external images are permitted. Inline CSS and JS — which every technique in these files depends on — are explicitly allowed.

Self-contained pages remain the right default regardless: an external image is a third party who can see every portal visitor's IP and user-agent, and a load-bearing one turns their outage into your broken login page.

No raw < outside a tag [verified]

The factory files contain zero raw < characters outside real tags. A file containing one — for (var i = 0; i < n; i++) is the obvious way to get there — was observed to break <pan_form/> substitution, leaving the login form dumped at the end of the document.

Consequences for anything generating these files:

  • No counting loops in JS. Use [].forEach.call(...).
  • No building markup in JS. el.innerHTML = '<a href=…>' is disqualified; put the markup in the body and have JS rewire it.
  • Comments count. Prose containing < is as dangerous as code containing it.

Auth methods that bypass these pages entirely [documented]

With SAML or Cloud Identity Engine, the browser is redirected to the IdP and login.esp never renders. Confirmed indirectly but solidly by a Palo Alto KB that exploits the redirect to suppress the portal page.

Customizing the login page is therefore worth doing when authentication is local, LDAP or RADIUS, or when Clientless VPN is in use. On a SAML-fronted portal it styles a page nobody loads; the branding lever there is the auth-response-page CLI (PAN-OS 10.2.11+, not on Panorama, not enabled by default).

The logout page still renders on SAML deployments.

Tooling

Working scripts live in tmp/gp-lab/ and are not part of the package:

Script Purpose
preflight.py All checks above, before upload. Knows both GP file shapes.
build.py Strips comments and indentation. Not a minifier — deliberately.
splice.py Reproduces login.esp / getsoftwarepage.esp locally from captured prefixes.
splice-logout.py Reproduces logout.esp locally.
make-logo.py Builds the scheme-aware logo as a data: URI.
probe.py Emits padded copies to test the import ceiling.

The simulators have twice invented failures the live page did not have — once by splicing the login prefix onto the download form, once through file:// breaking absolute asset paths. They are useful for iteration. Live verification is the arbiter.

Open questions

Question Why it matters
Does the 21,845-char import cap apply to URL Filtering pages too? If yes, MAX_BYTES = 17999 is unreachable — you could never import a file that large. One upload settles it; tmp/gp-lab/probe-urlfilter-17000.html is built for it.
What does the portal home page look like? Never captured. The lab redirects login straight to getsoftwarepage.esp. Its Bootstrap body is unstyled and deliberately left alone.
Does PAN-OS sanitize imports? [verified] no for CSS — a <style> block came back byte-identical, 5,456 B in, 5,456 B out. Untested for other constructs.
Which PAN-OS version was this? See Test environment. Unrecorded.