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

  1. Root init: Initialize-KrRoot -Path $PSScriptRoot so relative Assets/wwwroot resolves.
  2. Server & listener: New-KrServer; Add-KrEndpoint -Port 5000 -IPAddress Loopback.
  3. MIME map: Build $map adding YAML extensions and forcing .ps1 to text/plain.
  4. File server: Add-KrFileServerMiddleware -RequestPath '/' -RootPath '.\\Assets\\wwwroot' -EnableDirectoryBrowsing -ContentTypeMap $map.
  5. Cache headers: Append -Public -MaxAge 300 -MustRevalidate to emit: Cache-Control: public, max-age=300, must-revalidate.
  6. 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


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.