gps-license: GetPageSpeed license and update-telemetry module
Installation
You can install this module in any RHEL-based distribution, including, but not limited to:
- RedHat Enterprise Linux 7, 8, 9 and 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 and Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-gps-license
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-gps-license
Enable the module by adding the following at the top of /etc/nginx/nginx.conf:
load_module modules/ngx_http_gps_license_module.so;
This document describes nginx-module-gps-license v1.11.0 released on Oct 29 2026.
GetPageSpeed License Module for nginx. Validates JWT-based license tokens, reports usage telemetry, and exposes license state via nginx variables.
Overview
This module provides license validation for nginx-mod packages distributed via GetPageSpeed. It supports:
- JWT token validation with RS256 signatures
- Shared memory caching of license state
- Grace period for expired licenses (90 days)
- Soft/footer/hard enforcement modes
- nginx variables for license status
Note: Usage telemetry reporting is planned for a future version.
Configuration
Directives
gps_license_zone
Syntax: gps_license_zone zone=name:size
Context: http
Default: —
Defines shared memory zone for caching license state.
http {
gps_license_zone zone=license:1m;
}
gps_license
Syntax: gps_license on | off
Context: http, server, location
Default: off
Enables license checking for requests.
gps_license_token
Syntax: gps_license_token path | env:VAR_NAME
Context: http
Default: /etc/getpagespeed/license.jwt, then /etc/nginx/license.jwt
Path to JWT token file or environment variable name.
When the directive is absent, the module looks for the canonical
/etc/getpagespeed/license.jwt first (one file licenses every
GetPageSpeed product on the host — NGINX, Angie, PHP), then falls back
to the legacy /etc/nginx/license.jwt, which keeps working forever.
An explicit directive always wins over both.
gps_license_token /etc/getpagespeed/license.jwt;
## OR
gps_license_token env:GPS_LICENSE_TOKEN;
gps_license_enforcement
Syntax: gps_license_enforcement soft | footer | hard
Context: http, server, location
Default: soft
Enforcement mode:
- soft: Log warnings, continue serving traffic
- footer: Continue serving traffic, but append a visible GetPageSpeed
notice to unlicensed text/html responses (free-trial watermark). The
footer disappears as soon as a valid license token is installed.
Responses that are pre-compressed upstream (Content-Encoding set),
non-HTML, non-200, or subrequests are never touched. Locally produced
gzip is fine — the filter runs before the gzip filter.
- hard: Return 503 with error details
The backend report response may override the configured mode (field
enforcement: "soft" | "footer" | "hard"); a backend-pushed value takes
precedence over local config until the next restart.
gps_license_report_endpoint
Syntax: gps_license_report_endpoint url
Context: http
Default: https://www.getpagespeed.com/wp-json/getpagespeed/v1/license/report
URL for usage reporting (not yet implemented).
gps_license_report_interval
Syntax: gps_license_report_interval time
Context: http
Default: 1h
Interval between usage reports (not yet implemented).
gps_license_grace_period
Syntax: gps_license_grace_period time
Context: http
Default: 90d
Grace period after license expiration.
Variables
| Variable | Description |
|---|---|
$gps_license_valid |
"1" if valid or in grace, "0" otherwise |
$gps_license_tier |
License tier ("ultra", "plus", "standard") |
$gps_license_expires |
Expiration timestamp |
$gps_license_grace |
"1" if in grace period, "0" otherwise |
$gps_license_enforcement |
Current enforcement mode |
Example Configuration
http {
gps_license_zone zone=license:1m;
server {
listen 80;
server_name example.com;
gps_license on;
gps_license_token /etc/getpagespeed/license.jwt;
gps_license_enforcement soft;
# Log license state
log_format license '$remote_addr - $gps_license_tier [$gps_license_valid]';
location / {
root /var/www/html;
# Add license header for debugging
add_header X-License-Tier $gps_license_tier;
}
}
}
Error Responses
When gps_license_enforcement hard and license is invalid:
HTTP 503 with headers:
- X-GPS-License-Error: missing|invalid|expired|revoked
Response body:
GetPageSpeed License Error: expired
Your nginx-mod license is not valid.
Visit https://www.getpagespeed.com/repo-subscribe to get a license.
JWT Token Format
Tokens are signed with RS256 and contain:
{
"iss": "getpagespeed.com",
"aud": "nginx-mod",
"sub": "I-XXXXXXXXX",
"exp": 1234567890,
"tier": "ultra",
"jti": "uuid-v4",
"client_id": 12345,
"features": {
"reposync_allowed": true,
"pro_modules_allowed": true
}
}
features.pro_modules_allowed (v1.13.0) is the Pro-module runtime
entitlement: the module scans the loaded nginx modules against a baked-in
list of GetPageSpeed Pro modules (generated from the nginx-extras
plan: pro manifests) and mirrors the claim into shared memory. In the
current shadow phase an unentitled install with Pro modules loaded gets a
single startup [warn] plus pro_modules/pro_entitled fields in the
usage heartbeat — serving and enforcement are never affected. Tokens
minted before the claim existed parse as "unknown" and are never treated
as unentitled.
Public Key
The RSA public key is embedded at compile time. Key rotation requires module rebuild.
Important Notes
nginx Request Phases
The license module runs in the ACCESS_PHASE. Some nginx directives run in earlier phases and will bypass license checking:
returnandrewrite ... lastrun in REWRITE_PHASE (before ACCESS_PHASE)error_pageredirects may bypass the check
For license enforcement to work, use content handlers like proxy_pass,
fastcgi_pass, try_files, or static file serving with root/alias.
## WRONG - return runs before license check
location / {
gps_license on;
gps_license_enforcement hard;
return 200 "OK"; # Bypasses license check!
}
## CORRECT - static files trigger ACCESS_PHASE
location / {
gps_license on;
gps_license_enforcement hard;
root /var/www/html;
}
## CORRECT - proxy_pass triggers ACCESS_PHASE
location / {
gps_license on;
gps_license_enforcement hard;
proxy_pass http://backend;
}
gps_license_token Location
The gps_license_token directive must be in the http {} block (not in
server or location blocks) because license validation happens once at
worker startup, not per-request.