Response Compression
Reduce payload size (bandwidth + latency) for text-based responses by enabling HTTP content encoding (gzip, brotli, deflate) when the client sends an Accept-Encoding header.
The compression middleware sits early in the pipeline and, for eligible MIME types, wraps the response body stream so data is compressed on-the-fly.
| Aspect | Details |
|---|---|
| Enabled For HTTPS | Yes (opt-in with -EnableForHttps) |
| Negotiation | Chooses first supported algorithm from client Accept-Encoding (br, gzip, deflate) |
| Typical Savings | 60–95% for verbose JSON / HTML / XML payloads |
| CPU Cost | Low for gzip, higher for brotli (better ratio) |
| Skip Heuristics | Very small bodies may be skipped (no Content-Encoding) |
Binary formats (e.g. already-compressed images) are not included by default; focus on text-centric content types.
Full source
File: pwsh/tutorial/examples/10.2-Compression.ps1
<#
Compression Demo Script
Demonstrates response compression for multiple content types.
Routes return sufficiently large payloads so you can observe the
`Content-Encoding` header (gzip/brotli/deflate) when the client
sends an appropriate `Accept-Encoding`.
Try (PowerShell 7+):
irm https://127.0.0.1:5000/text -SkipCertificateCheck -MaximumRedirection 0 -Headers @{ 'Accept-Encoding'='gzip' } -Verbose
(Invoke-WebRequest -Uri https://127.0.0.1:5000/json -SkipCertificateCheck -Headers @{ 'Accept-Encoding'='br, gzip' }).Headers.'Content-Encoding'
NOTE: Very small responses may be excluded from compression depending
on server heuristics; all payloads below are padded to be >1KB.
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
# 1. Logging (console only for clarity)
New-KrLogger |
Add-KrSinkConsole |
Register-KrLogger -Name 'console' -SetAsDefault
# 2. Server
New-KrServer -Name 'Compression Demo'
# 3. Listener (HTTP + self-signed HTTPS)
Add-KrEndpoint -Port $Port -IPAddress $IPAddress -SelfSignedCert | Out-Null
<#
.SYNOPSIS
Generate a large block of text by repeating a seed string.
.PARAMETER seed
The seed string to repeat.
.PARAMETER repeat
The number of times to repeat the seed string (default: 80).
.RETURNS
A large string composed of the seed repeated.
#>
function _NewLargeBlock([string]$seed, [int]$repeat = 80) {
return (($seed + ' ') * $repeat).Trim()
}
# 5. Compression middleware (cover common textual MIME types)
Add-KrCompressionMiddleware -EnableForHttps -MimeTypes @('text/plain', 'text/html', 'application/json', 'application/xml', 'application/x-www-form-urlencoded')
# 6. Lock in baseline configuration
Enable-KrConfiguration
# /text (plain text)
Add-KrMapRoute -Verbs Get -Pattern '/text' -ScriptBlock {
$body = _NewLargeBlock 'PlainText payload demonstrating compression' 120
Write-Verbose "[/text] length=$($body.Length)" -Verbose
Write-KrTextResponse -InputObject $body -StatusCode 200
}
# /json (JSON array)
Add-KrMapRoute -Verbs Get -Pattern '/json' -ScriptBlock {
$items = 1..150 | ForEach-Object { [pscustomobject]@{ id = $_; message = 'Compression JSON sample record ' + $_ } }
Write-Verbose "[/json] count=$($items.Count)" -Verbose
Write-KrJsonResponse -InputObject $items -StatusCode 200
}
# /html (HTML page)
Add-KrMapRoute -Verbs Get -Pattern '/html' -ScriptBlock {
$paras = 1..40 | ForEach-Object { '<p>Lorem ipsum dolor sit amet (para ' + $_ + ')</p>' }
$html = @"
<!DOCTYPE html>
<html>
<head><meta charset='utf-8'><title>Compression Demo</title></head>
<body>
<h1>HTML Compression Demo</h1>
$($paras -join "`n")
</body>
</html>
"@
Write-KrHtmlResponse -Template $html -StatusCode 200
}
# /xml (XML serialization of objects)
Add-KrMapRoute -Verbs Get -Pattern '/xml' -ScriptBlock {
$data = 1..120 | ForEach-Object { [pscustomobject]@{ Index = $_; Text = 'Sample XML record ' + $_ } }
Write-Verbose "[/xml] count=$($data.Count)" -Verbose
Write-KrXmlResponse -InputObject $data -StatusCode 200
}
# /form (url-encoded style body)
Add-KrMapRoute -Verbs Get -Pattern '/form' -ScriptBlock {
$pairs = 1..200 | ForEach-Object { "k$($_)=value$($_)" }
$payload = ($pairs -join '&')
Write-Verbose "[/form] pairs=$($pairs.Count) length=$($payload.Length)" -Verbose
$Context.Response.ContentType = 'application/x-www-form-urlencoded'
$Context.Response.Body = $payload
$Context.Response.StatusCode = 200
}
# /raw-nocompress (large payload explicitly excluded from compression)
Add-KrMapRoute -Options (New-KrMapRouteOption -Property @{
Pattern = '/raw-nocompress'
HttpVerbs = 'Get'
ScriptCode = @{
Code = {
$body = _NewLargeBlock 'This route intentionally disables response compression.' 150
Write-Verbose "[/raw-nocompress] length=$($body.Length)" -Verbose
Write-KrTextResponse -InputObject $body -StatusCode 200 -ContentType 'text/plain'
}
Language = 'PowerShell'
}
DisableResponseCompression = $true
})
# /info (reflect Accept-Encoding to show negotiation)
Add-KrMapRoute -Verbs Get -Pattern '/info' -ScriptBlock {
$ae = Get-KrRequestHeader -Name 'Accept-Encoding'
$body = [pscustomobject]@{
AcceptEncoding = $ae
Tip = 'Use -Headers @{"Accept-Encoding"="gzip"} (or br,deflate) to see compressed responses.'
Routes = '/text,/json,/html,/xml,/form,/raw-nocompress'
NonCompressedExample = '/raw-nocompress'
}
Write-KrJsonResponse -InputObject $body -StatusCode 200
}
# Start server
Start-KrServer -CloseLogsOnExit
# Close default logger when process exits
Close-KrLogger
Step-by-step
- Add the compression middleware with desired MIME types and HTTPS behavior.
- Map routes that emit sizable text/JSON/HTML/XML bodies for meaningful compression.
- Commit configuration and start the server.
- Call routes with
Accept-Encodingheader (e.g.,gzip,br) to negotiate compression. - Inspect
Content-Encodingon responses to verify compression.
Try it
| Route | Type | Notes |
|---|---|---|
/text | text/plain | Large repeated block >1KB |
/json | application/json | Array of objects (n=150) |
/html | text/html | Many <p> tags to grow payload |
/xml | application/xml | Serialized collection |
/form | application/x-www-form-urlencoded | Key=value pairs joined with & |
/info | application/json | Echoes Accept-Encoding to aid debugging |
How negotiation works
- Client sends
Accept-Encoding: br, gzip, deflate(order denotes preference). - Server picks first matching implementation it supports.
- Middleware sets
Content-Encodingheader and compresses the body stream. - Size-sensitive heuristics (or excluded MIME type) → response left uncompressed.
Open a terminal and run the sample (PowerShell 7+):
pwsh .\docs\_includes\examples\pwsh\10.2-Compression.ps1
Then exercise routes (add -Headers @{ 'Accept-Encoding'='gzip' } or include brotli):
# Plain text
(Invoke-WebRequest https://127.0.0.1:5000/text -SkipCertificateCheck -Headers @{ 'Accept-Encoding'='gzip' }).Headers.'Content-Encoding'
# JSON
(Invoke-WebRequest https://127.0.0.1:5000/json -SkipCertificateCheck -Headers @{ 'Accept-Encoding'='br, gzip' }).Headers.'Content-Encoding'
# HTML
(Invoke-WebRequest https://127.0.0.1:5000/html -SkipCertificateCheck -Headers @{ 'Accept-Encoding'='gzip' }).Headers.'Content-Encoding'
# Info (see what you sent)
Invoke-RestMethod https://127.0.0.1:5000/info -SkipCertificateCheck -Headers @{ 'Accept-Encoding'='gzip' }
Testing strategy
Key assertions for automated tests:
- Eligible routes return status 200.
- Without
Accept-Encoding, either noContent-Encodingor allowed default. - With
Accept-Encoding: gzip, response includesContent-Encoding: gzipfor large bodies. /inforeflects the header value.- Body lengths differ (compressed smaller) — len(compressed) < len(uncompressed) for same route.
Disabling / tuning
Omit routes or MIME types not desired:
Add-KrCompressionMiddleware -MimeTypes @('text/plain','application/json')
Skip HTTPS compression by removing -EnableForHttps (some platforms historically disabled HTTPS compression to mitigate certain attacks; modern mitigations usually make it safe for typical APIs).
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| No Content-Encoding header | Body below minimum size / MIME type excluded | Increase payload size or add MIME type |
| Large CPU increase | Brotli at high quality | Prefer gzip in latency-sensitive APIs |
| Double compression | Reverse proxy already compressing | Disable at one layer |
References
Previous / Next
Previous: Antiforgery Protection Next: Rate Limiting (planned)