Host Filtering
Restrict which Host headers are allowed to reach your app. This mitigates HTTP Host header attacks and ensures requests are routed only for expected hostnames.
Full source
File: pwsh/tutorial/examples/10.6-HostFiltering.ps1
<#
Sample Kestrun Server – Host Filtering
Demonstrates enabling Host Filtering middleware and a simple route.
FileName: 10.6-HostFiltering.ps1
#>
param(
[int]$Port = 5000,
[IPAddress]$IPAddress = [IPAddress]::Loopback
)
# Optional: console logger so we can see events
New-KrLogger | Set-KrLoggerLevel -Value Debug |
Add-KrSinkConsole |
Register-KrLogger -Name 'console' -SetAsDefault | Out-Null
# 1) Create server
New-KrServer -Name 'Endpoints HostFiltering'
# 2) Listener
Add-KrEndpoint -Port $Port -IPAddress $IPAddress
# 3) Configure Host Filtering via parameters
# - Only allow requests with Host header "example.com" or "www.example.com"
# - Reject empty Host headers
# - Exclude failure message body for blocked hosts
Add-KrHostFiltering `
-AllowedHosts @('example.com', 'www.example.com') `
-NotAllowEmptyHosts
# 4) Enable Kestrun configuration
Enable-KrConfiguration
# 5) Map a simple route
Add-KrMapRoute -Verbs Get -Pattern '/hello' -ScriptBlock {
Write-KrLog -Level Information -Message 'Processing /hello request'
Write-KrTextResponse -InputObject 'Hello from host-filtered server' -StatusCode 200
}
# 6) Run
Start-KrServer -CloseLogsOnExit
Step-by-step
- Add the Host Filtering middleware with your allowlist.
- Commit configuration with
Enable-KrConfiguration. - Map routes as usual; unauthorized hosts never reach them.
- Start the server and probe with different Host headers.
Try it
Run the sample:
pwsh ./docs/_includes/examples/pwsh/10.6-HostFiltering.ps1
Then test allowed vs. blocked hosts:
# Allowed
curl -i -H "Host: example.com" http://127.0.0.1:5000/hello
curl -i -H "Host: www.example.com" http://127.0.0.1:5000/hello
# Blocked
curl -i -H "Host: blocked.example" http://127.0.0.1:5000/hello
# Empty Host (rejected when -NotAllowEmptyHosts used)
curl -i -H "Host: " http://127.0.0.1:5000/hello
# Allowed
Invoke-WebRequest http://127.0.0.1:5000/hello -Headers @{ Host = 'example.com' }
Invoke-WebRequest http://127.0.0.1:5000/hello -Headers @{ Host = 'www.example.com' }
# Blocked
Invoke-WebRequest http://127.0.0.1:5000/hello -Headers @{ Host = 'blocked.example' } -SkipHttpErrorCheck
# Empty Host (rejected when -NotAllowEmptyHosts used)
Invoke-WebRequest http://127.0.0.1:5000/hello -Headers @{ Host = '' } -SkipHttpErrorCheck
Expected outcomes:
- Allowed hosts → 200 OK with response body.
- Disallowed or empty host → 400 Bad Request. If
-ExcludeFailureMessageis used, the response body won’t include details.
Key Points
The middleware validates the request Host header against an allowlist and returns 400 Bad Request for non-matching hosts. You can optionally allow empty Host headers and include a failure message.
| Option | Meaning |
|---|---|
| AllowedHosts | List of hostnames to allow (no ports). Supports wildcard * for any non-empty, and subdomain wildcards like *.example.com. |
| NotAllowEmptyHosts | If set, requests with an empty Host header are rejected. |
| ExcludeFailureMessage | If set, the 400 response omits the failure message body. |
IPv6 literals must be bracketed and normalized; Unicode hosts are matched using their punycode representation.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Still seeing 200 for blocked host | Middleware added after configuration or not applied | Ensure Add-KrHostFiltering is called before Enable-KrConfiguration. |
| Allowed host with port is blocked | Ports are not permitted in AllowedHosts | Remove the port; use just the hostname. |
| Wildcard not matching parent domain | *.example.com matches subdomains, not the bare example.com | Add both example.com and *.example.com if needed. |
References
- Add-KrHostFiltering
- Enable-KrConfiguration
- Add-KrMapRoute
- Start-KrServer
- Write-KrTextResponse
- Write-KrLog
- New-KrLogger
- Set-KrLoggerLevel
- Add-KrSinkConsole
- Register-KrLogger
- RoutingTopic
Previous / Next
Previous: SignalR Next: None