File Server Caching Headers
Add HTTP cache directives (Cache-Control) directly to static file responses served by the file server middleware. For a deeper conceptual guide (layer interactions, validators, decision matrix) see the HTTP Caching guide. This chapter focuses on configuring default caching for all files (and directory listings) exposed through Add-KrFileServerMiddleware using its built‑in switches.
Prerequisites: see Introduction.
Full source
File: pwsh/tutorial/examples/3.5-File-ServerCaching.ps1
<#
Sample Kestrun Server on how to configure a static file server.
These examples demonstrate how to configure static routes with directory browsing in a Kestrun server.
FileName: 3.2-File-Server.ps1
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
# Initialize Kestrun root directory
# the default value is $PWD
# This is recommended in order to use relative paths without issues
Initialize-KrRoot -Path $PSScriptRoot
# Create a new Kestrun server
New-KrServer -Name "Simple Server"
# Add a listener on the configured port and IP address
Add-KrEndpoint -Port $Port -IPAddress $IPAddress
# Define the content type map to add to the default set
$map = @{
".yaml" = "application/x-yaml"
".yml" = "application/x-yaml"
".ps1" = "text/plain"
}
# Add a file server with browsing enabled
# set Cache-Control headers to public, max-age 300, must-revalidate
Add-KrFileServerMiddleware -RequestPath '/' -RootPath '.\Assets\wwwroot' -EnableDirectoryBrowsing -ContentTypeMap $map -Public -MaxAge 300 -MustRevalidate
# Enable Kestrun configuration
Enable-KrConfiguration
# Start the server asynchronously
Start-KrServer
What this sample shows
- Mapping a root file server with directory browsing enabled
- Extending MIME types with a custom content type map
- Applying cache semantics (public, max-age, must-revalidate) to every served file
- Separation of concerns: file server level headers vs per-response dynamic caching (next chapter)
Step-by-step
- Root init:
Initialize-KrRoot -Path $PSScriptRootso relativeAssets/wwwrootresolves. - Server & listener:
New-KrServer;Add-KrEndpoint -Port 5000 -IPAddress Loopback. - MIME map: Build
$mapadding YAML extensions and forcing.ps1totext/plain. - File server:
Add-KrFileServerMiddleware -RequestPath '/' -RootPath '.\\Assets\\wwwroot' -EnableDirectoryBrowsing -ContentTypeMap $map. - Cache headers: Append
-Public -MaxAge 300 -MustRevalidateto emit:Cache-Control: public, max-age=300, must-revalidate. - Enable & start:
Enable-KrConfiguration;Start-KrServer.
Try it
Launch the script then inspect responses:
pwsh .\3.5-File-ServerCaching.ps1
In a new terminal:
# Any static file (body truncated for brevity)
Invoke-WebRequest -Uri 'http://127.0.0.1:5000/files/sample.txt' | Select-Object -ExpandProperty RawContent | Select-String 'Cache-Control'
# Directory listing (still carries cache headers)
Invoke-WebRequest -Uri 'http://127.0.0.1:5000/' -Method Get | Select-Object -ExpandProperty RawContent | Select-String 'Cache-Control'
Curl examples:
curl -I http://127.0.0.1:5000/files/sample.txt
curl -I http://127.0.0.1:5000/
Expected header snippet:
Cache-Control: public, max-age=300, must-revalidate
How it works
Add-KrFileServerMiddleware accepts cache-control switches. When present it constructs a CacheControlHeaderValue and applies it to every served response (including 200 file hits and 404 file not found replies where applicable). This provides coarse‑grained defaults. Finer control (per dynamic route or conditional revalidation) is handled in Response Caching.
Choosing directives
| Directive | When to use | Caution |
|---|---|---|
-Public | Same file content for all users | Avoid for personalized or auth‑gated content |
-Private | User/time specific variation | Mutually exclusive with -Public |
-MaxAge <sec> | Frequently requested, moderately static assets | Keep short for rapidly changing files |
-MustRevalidate | Clients must revalidate after staleness | Adds latency if clients frequently revalidate |
-NoStore | Sensitive data; do not cache at all | Overrides most other directives |
-NoCache | Allow caching but force validation before reuse | Combine with validators (ETag/Last-Modified) |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Missing Cache-Control header | No cache switches passed | Add -Public / -Private / age directives |
| Directives not what you expect | Re-ran without restarting server | Restart after changing middleware switches |
| Directory listing not updating | Browser cached aggressively | Hard refresh or lower -MaxAge |
| Private content cached publicly | Used -Public with personalized files | Switch to -Private or remove caching directives |
References
- Add-KrFileServerMiddleware
- Initialize-KrRoot
- Add-KrEndpoint
- New-KrServer
- Enable-KrConfiguration
- Start-KrServer
Additional examples
| Scenario | Example |
|---|---|
| Long‑lived versioned assets | -Public -MaxAge 86400 (use hashed/ versioned filenames) |
| Sensitive static content (should not persist) | -NoStore -Private |
| CDN override freshness | -Public -MaxAge 60 -SharedMaxAge 600 |
| Require revalidation after TTL | -Public -MaxAge 120 -MustRevalidate |
Previous / Next
Go back to Adding a Favicon or continue to Response Caching.