Site icon GetPageSpeed

NGINX SXG Module: Signed HTTP Exchanges Guide

NGINX SXG Module: Enable Signed HTTP Exchanges for Instant Page Loads

Signed HTTP Exchanges (SXG) represent a web standard that enables browsers to prefetch and cache content from a different origin while maintaining cryptographic proof of authenticity. With the NGINX SXG module, your server can automatically convert upstream responses into the SXG format. This enables Google Search and other platforms to prefetch your pages for near-instant navigation.

Important Notice: SXG faces significant long-term uncertainty due to industry-wide OCSP deprecation. See Current Status and Future Outlook before investing in SXG infrastructure.

What Are Signed HTTP Exchanges?

Signed HTTP Exchanges allow a distributor (like Google’s cache) to serve your content on your behalf while providing cryptographic proof that the content hasn’t been tampered with. When a user clicks a search result that supports SXG, the browser can display your content instantly because it was already prefetched—all while showing your original URL in the address bar.

The key benefits include:

How the NGINX SXG Module Works

The ngx_http_sxg_filter_module from Google operates as an NGINX filter module. When configured, it intercepts responses from your upstream application and converts them into the SXG format—but only when the client explicitly requests it.

Here’s the request flow:

  1. A client (typically Google’s crawler or a supporting browser) sends a request with the header Accept: application/signed-exchange;v=b3
  2. NGINX proxies the request to your upstream application
  3. The upstream returns a normal HTML response
  4. The SXG module signs the response using your private key and certificate
  5. NGINX returns the signed exchange to the client

The module also handles subresource integrity automatically. When your response contains Link: rel="preload" headers, the module fetches those subresources and calculates their integrity hashes. It then includes the signatures in the response, enabling distributors to prefetch not just the main document but also critical resources like CSS and JavaScript.

Prerequisites for SXG

Before deploying SXG, ensure you have the following:

SXG-Specific Certificate

Standard TLS certificates won’t work for SXG—including Let’s Encrypt. You need a certificate with the CanSignHttpExchanges extension, which only DigiCert currently offers. The certificate must also use ECDSA (P-256 or P-384)—RSA certificates are not supported.

Important: Let’s Encrypt does not issue SXG certificates. Their certificates lack the required CanSignHttpExchanges extension. You must purchase a dedicated SXG certificate from DigiCert.

HTTPS-Only

Both the sxg_cert_url and sxg_validity_url must use HTTPS. The validity URL must also be on the same origin as your website.

Upstream Application

The SXG module requires dynamic content from an upstream application—it filters proxied responses. Static file serving bypasses the filter chain, so you’ll need proxy_pass or fastcgi_pass configurations.

CBOR Certificate Chain

SXG requires a CBOR-encoded certificate chain file that browsers fetch to verify signatures. With DigiCert certificates, NGINX generates this automatically using the sxg_cert_path directive—no manual steps needed. See the sxg_cert_path directive below.

Installation

RHEL, CentOS, AlmaLinux, Rocky Linux

Install the module from the GetPageSpeed repository:

sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-sxg

Then load the module in your /etc/nginx/nginx.conf:

load_module modules/ngx_http_sxg_filter_module.so;

For more information, see the RPM module page.

Debian and Ubuntu

First, set up the GetPageSpeed APT repository, then install:

sudo apt-get update
sudo apt-get install nginx-module-sxg

On Debian/Ubuntu, the package handles module loading automatically. No load_module directive is needed.

For more information, see the APT module page.

Configuration Directives

The SXG module provides nine configuration directives. Most must be placed at the server context level.

sxg

Enables or disables the SXG module for the current context.

Syntax: sxg on | off;
Default: off
Context: server, location

server {
    sxg on;
    # ... other directives
}

When disabled, NGINX serves responses normally without SXG conversion, regardless of the client’s Accept header.

sxg_certificate

Specifies the path to your SXG certificate file in PEM format.

Syntax: sxg_certificate path;
Default: —
Context: server

sxg_certificate /etc/nginx/sxg/example.com.pem;

This certificate must have the CanSignHttpExchanges extension and use ECDSA. Self-signed certificates will work for testing but won’t be trusted by browsers or search engines.

sxg_certificate_key

Specifies the path to the private key corresponding to your SXG certificate.

Syntax: sxg_certificate_key path;
Default: —
Context: server

sxg_certificate_key /etc/nginx/sxg/example.com.key;

The private key must be an ECDSA key matching your certificate. Keep this file secure with restricted permissions (chmod 600).

sxg_cert_url

The HTTPS URL where the CBOR-encoded certificate chain can be fetched.

Syntax: sxg_cert_url url;
Default: —
Context: server

sxg_cert_url https://example.com/.well-known/sxg-certs/example.com.cbor;

This URL is embedded in the signed exchange and tells clients where to fetch the certificate for verification. The URL must use HTTPS.

sxg_validity_url

The HTTPS URL for validity information, used by clients to check if an SXG is still valid.

Syntax: sxg_validity_url url;
Default: —
Context: server

sxg_validity_url https://example.com/.well-known/sxg-validity;

This URL must be on the same origin as your website and must use HTTPS.

sxg_cert_path

Tells NGINX to automatically generate and serve the CBOR-encoded certificate chain at this path. This is the recommended approach for production with DigiCert certificates.

Syntax: sxg_cert_path path;
Default: —
Context: server

sxg_cert_path /.well-known/sxg-certs/example.com.cbor;

When this directive is set, NGINX:
1. Fetches OCSP responses from your certificate’s OCSP responder (e.g., DigiCert)
2. Generates the CBOR certificate chain automatically
3. Serves it at the specified path
4. Refreshes the OCSP response periodically before it expires

This is all you need for production. No manual CBOR generation, no cron jobs, no external tools. Just point sxg_cert_url to the same path and NGINX handles everything.

Requirements:
– Your certificate must have a valid OCSP responder URL (DigiCert certificates have this)
– NGINX must be able to reach the OCSP responder over the network

Note: For testing with self-signed certificates (which have no OCSP responder), see Manual CBOR Generation for Testing below.

sxg_expiry_seconds

Sets the lifetime of generated SXG responses in seconds.

Syntax: sxg_expiry_seconds seconds;
Default: 86400 (1 day)
Context: server

sxg_expiry_seconds 604800;

The maximum allowed value is 604800 seconds (7 days)—this limit is enforced by the SXG specification. NGINX will refuse to start if you configure a higher value.

For content containing JavaScript, Google recommends setting this to less than 1 day (86400 seconds) to ensure users receive updates promptly.

sxg_fallback_host

Specifies the hostname used in the fallback URL embedded in SXG responses.

Syntax: sxg_fallback_host hostname;
Default: value from Host header
Context: server

sxg_fallback_host example.com;

The fallback URL tells the browser where to navigate if the signed exchange fails verification. By default, it uses the Host header from the incoming request.

sxg_max_payload

Sets the maximum response body size that the module will convert to SXG.

Syntax: sxg_max_payload size;
Default: 67108864 (64 MiB)
Context: server

sxg_max_payload 128m;

Responses larger than this limit will be served normally without SXG conversion. The default of 64 MiB is suitable for most web pages.

Complete Configuration Example

Here’s a production-ready configuration that enables SXG for a website. Note how sxg_cert_path handles the CBOR certificate automatically:

load_module modules/ngx_http_sxg_filter_module.so;

worker_processes auto;
error_log /var/log/nginx/error.log;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Required for subresource prefetching
    subrequest_output_buffer_size 4096k;

    upstream backend {
        server 127.0.0.1:8080;
    }

    server {
        listen 443 ssl;
        server_name example.com;

        # Standard TLS configuration
        ssl_certificate /etc/nginx/ssl/example.com.pem;
        ssl_certificate_key /etc/nginx/ssl/example.com.key;

        # SXG configuration
        sxg on;
        sxg_certificate /etc/nginx/sxg/example.com-sxg.pem;
        sxg_certificate_key /etc/nginx/sxg/example.com-sxg.key;
        sxg_cert_url https://example.com/.well-known/sxg-certs/cert.cbor;
        sxg_validity_url https://example.com/.well-known/sxg-validity;
        sxg_cert_path /.well-known/sxg-certs/cert.cbor;  # NGINX generates CBOR automatically
        sxg_expiry_seconds 86400;
        sxg_fallback_host example.com;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

Important Configuration Notes

subrequest_output_buffer_size: This directive is essential for subresource prefetching. The SXG module uses subrequests to fetch linked resources (CSS, JS) and calculate their integrity hashes. Set this to at least 4096k to handle typical subresource sizes.

Separate certificates: Note that you need two separate certificates—one for standard TLS (ssl_certificate) and one for SXG (sxg_certificate). These serve different purposes and have different requirements.

Testing Your SXG Configuration

Verify Module Loading

The SXG module is a dynamic module. To verify it’s loaded correctly:

  1. Check the module file exists:
    ls /usr/lib64/nginx/modules/ngx_http_sxg_filter_module.so
    
  2. Test config with module directives — this is the definitive test:
    nginx -t
    

    If nginx -t passes and your config contains sxg on; plus other SXG directives, the module is loaded and working. If the module wasn’t loaded, you’d see “unknown directive” errors.

  3. Reload and test at runtime:

    systemctl reload nginx
    

Test SXG Response

Send a request with the SXG Accept header:

curl -H "Accept: application/signed-exchange;v=b3,*/*;q=0.8" \
     -H "Host: example.com" \
     https://your-server/ -I

A successful SXG response includes these headers:

Content-Type: application/signed-exchange;v=b3
X-Content-Type-Options: nosniff
Vary: Accept

Use the SXG Validator Extension

Google provides an SXG Validator Chrome extension that checks your SXG responses for cache compatibility and specification compliance.

Query Google’s SXG Cache

To verify that Google can serve your SXG content, query the webpkgcache directly:

https://your-domain-com.webpkgcache.com/doc/-/s/your-domain.com/page

Replace your-domain.com with your actual domain (using hyphens for dots in the subdomain).

Subresource Prefetching

One of the most powerful features of the NGINX SXG module is automatic subresource handling. When your upstream response includes Link headers with rel="preload", the module:

  1. Fetches each linked resource via NGINX subrequests
  2. Calculates the integrity hash for each resource
  3. Appends rel="allowed-alt-sxg" with header-integrity to the response

This enables distributors like Google to prefetch not just your HTML but also the critical CSS and JavaScript, dramatically improving perceived performance.

For example, if your upstream returns:

Link: </style.css>;rel="preload";as="style"

The SXG module will automatically add:

Link: </style.css>;rel="preload";as="style",</style.css>;rel="allowed-alt-sxg";header-integrity="sha256-..."

Performance Considerations

CPU Impact

SXG signing uses ECDSA cryptography, which adds CPU overhead to each request. However, because SXG is only generated when clients specifically request it (via the Accept header), the impact is limited to crawler traffic and prefetch requests—not your regular user traffic.

Memory Usage

The module buffers the entire response body before signing. Configure sxg_max_payload appropriately based on your typical page sizes and available memory.

Caching Strategy

Consider using NGINX’s proxy cache to cache SXG responses. Since SXG responses include a Vary: Accept header, NGINX can maintain separate cache entries for regular and SXG versions.

Current Status and Future Outlook

Warning: SXG faces significant long-term uncertainty. Before investing in SXG infrastructure, consider these developments:

OCSP Deprecation Threatens SXG

The SXG specification requires OCSP (Online Certificate Status Protocol) for certificate validation. The CBOR certificate chain format embeds OCSP responses, and the spec states that “OCSP stapling to ensure the validity of the certificates is required for SXGs.”

However, the industry is abandoning OCSP in favor of CRLs (Certificate Revocation Lists):

The reasons for this shift include privacy concerns (OCSP reveals which sites users visit) and operational simplicity.

Current SXG Viability

Component Status
DigiCert SXG Certificates Still available with OCSP
Google Search SXG Support Active
Chrome/Chromium SXG Support Active (since Chrome 73)
Cloudflare SXG Deprecated (October 2025)
Let’s Encrypt OCSP Dead (August 2025)

SXG currently works because DigiCert (the only SXG certificate provider) still operates OCSP responders. However, if DigiCert follows the industry trend and deprecates OCSP, SXG will break with no clear migration path.

Recommendation

If you’re considering SXG deployment:

  1. Evaluate ROI carefully — The technology works today but has uncertain longevity
  2. Monitor the ecosystem — Subscribe to the webpackaging-announce mailing list for updates
  3. Consider alternatives — For performance, explore Speculation Rules API which doesn’t require special certificates

For sites heavily dependent on Google Search traffic where instant page loads provide measurable competitive advantage, SXG may still be worth deploying—but plan for the possibility that the technology may become obsolete.

Troubleshooting

“failed to load private key”

Ensure your private key file:
– Exists at the specified path
– Has correct permissions (chmod 600)
– Is an ECDSA key (not RSA)
– Matches the certificate

“too long lifespan of SXG”

The sxg_expiry_seconds value exceeds 604800 (7 days). Reduce it to comply with the SXG specification.

“failed to load Cert-Chain”

When using sxg_cert_path:
– Ensure your NGINX server can reach the certificate’s OCSP responder
– Self-signed certificates don’t support this feature
– Check that the certificate chain is complete

SXG Not Being Generated

If requests aren’t returning application/signed-exchange;v=b3:
– Verify sxg on; is set
– Check that all required directives are configured
– Confirm the request includes the correct Accept header
– Ensure you’re proxying to an upstream (static files bypass the filter)

Manual CBOR Generation for Testing

This section is only needed for testing with self-signed certificates, which don’t have OCSP responders. For production with DigiCert certificates, use sxg_cert_path instead—it handles everything automatically.

Install the SXG Tools

The tools require Go 1.18 or later:

go install github.com/WICG/webpackage/go/signedexchange/cmd/gen-certurl@latest

By default, Go installs binaries to ~/go/bin/. Add this to your PATH:

export PATH=$PATH:~/go/bin

Create a Self-Signed Test Certificate

Generate an ECDSA certificate for testing:

openssl ecparam -name prime256v1 -genkey -out test.key
openssl req -new -x509 -key test.key -out test.pem -days 365 -subj "/CN=example.com"

Generate CBOR with Dummy OCSP

For testing only—browsers won’t trust this in production:

mkdir -p /var/www/.well-known/sxg-certs
gen-certurl -pem test.pem -ocsp <(echo "ocsp") > /var/www/.well-known/sxg-certs/cert.cbor

Serve the CBOR File

Configure NGINX to serve the manually-generated CBOR file:

location /.well-known/sxg-certs/ {
    root /var/www;
    types {
        application/cert-chain+cbor cbor;
    }
    add_header Cache-Control "public, max-age=86400";
}

SELinux Note (RHEL/Rocky/AlmaLinux): Set the correct SELinux context:

restorecon -Rv /var/www/.well-known

Remember: This is for local testing only. Self-signed certificates won’t work with Google Search or real browsers. For production, purchase a DigiCert SXG certificate and use sxg_cert_path.

For more NGINX performance optimization techniques, see:

Conclusion

The NGINX SXG module enables Signed HTTP Exchanges, allowing search engines and CDNs to prefetch your content for instant page loads while maintaining cryptographic authenticity. While the technology works today and can provide measurable performance benefits for search-driven traffic, the long-term future is uncertain due to the industry’s move away from OCSP.

For production deployment, you need a special certificate with the CanSignHttpExchanges extension from DigiCert. Test thoroughly using the SXG Validator extension and Google’s webpkgcache before rolling out to production. Monitor the ecosystem closely and have contingency plans for alternative prefetching technologies.

The module source code is available on GitHub.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Exit mobile version