Skip to content

Static File Serving

Overview

HttpServer has built-in static file serving. When WebRoot is configured, it automatically handles static resource requests without writing controllers.

Version Change Notice

Starting from vbman 1.0.0.419, WebRoot has been separated from the Start() method into a chained function. The old syntax Server.Start 8080, "C:\WebRoot" is no longer supported. Please use Server.WebRoot("C:\WebRoot").Start 8080 instead.

Quick Configuration

vb
Private Sub Form_Load()
    Set Server = New cHttpServer

    ' Configure static file root directory
    Server.WebRoot("C:\WebRoot").Start 8080

    Debug.Print "Static file service: http://localhost:8080/"
End Sub

Directory Structure Example

C:\WebRoot\
├── index.html          # Home page
├── favicon.ico         # Site icon
├── css\
│   ├── style.css
│   └── theme.css
├── js\
│   ├── app.js
│   └── utils.js
├── images\
│   ├── logo.png
│   └── banner.jpg
└── upload\
    └── avatar.png

Request Mapping

Request URLMaps to File
/C:\WebRoot\index.html
/css/style.cssC:\WebRoot\css\style.css
/js/app.jsC:\WebRoot\js\app.js
/images/logo.pngC:\WebRoot\images\logo.png

Supported MIME Types

vb
' System automatically identifies Content-Type for the following file types

' Text types
text/html          -> .html, .htm
text/css           -> .css
text/javascript    -> .js
text/plain         -> .txt

' Image types
image/png          -> .png
image/jpeg         -> .jpg, .jpeg
gif/image          -> .gif
image/svg+xml      -> .svg
image/x-icon       -> .ico

' Application types
application/json   -> .json
application/xml    -> .xml
application/pdf    -> .pdf

' Font types
font/woff2         -> .woff2
font/woff          -> .woff

Default Document Mechanism

When the request path corresponds to a directory (e.g., accessing root path /), HttpServer automatically searches for default documents in priority order:

PriorityDefault Document
1index.html
2index.htm
3default.html
4default.htm

Processing Flow

Request "/" or "/subdir/"


Check if WebRoot + path is a directory

   ├─> Is directory → Iterate default document list
   │     ├─> index.html exists? → Return file ✅
   │     ├─> index.htm exists? → Return file ✅
   │     ├─> default.html exists? → Return file ✅
   │     ├─> default.htm exists? → Return file ✅
   │     └─> None exist → Return 403 "Directory listing not allowed" 🔒

   └─> Not directory → Return 404

Adding Custom Default Documents

vb
Private Sub Form_Load()
    Set Server = New cHttpServer

    ' Add custom default document (e.g., home.html)
    Server.AddDefaultDocument "home.html"
    ' Duplicate additions are automatically ignored

    Server.WebRoot("C:\WebRoot").Start 8080
End Sub

After adding, the default document search order becomes: index.html → index.htm → default.html → default.htm → home.html

Directory Redirect

When the request path corresponds to a physical directory but doesn't end with /, HttpServer automatically 302 redirects to the path with /:

Request /subdir (corresponds to physical directory)
   → 302 redirect to /subdir/
   → Then search for default documents like /subdir/index.html

Note: Root path / doesn't need redirect, directly enters default document search.

Priority Relationship with Routes

If a manual route is registered for root path /, the route executes first and won't enter default document search:

vb
' Route priority: root path handled by controller
Call Server.Router.Add("/", "Home@Index")

' Now accessing "/" → Calls Home.Index, doesn't search for index.html

Priority Explanation

Static files take priority over route matching:

Request /index.html

   ├──> Check if C:\WebRoot\index.html exists
   │     ├─> Exists -> Return static file
   │     └─> Does not exist -> Go to route matching

   └──> Match route /index.html

Mixed Mode (Static Files + API)

vb
Private Sub Form_Load()
    Set Server = New cHttpServer

    ' ========== Configure Routes ==========
    ' API controllers
    Call Server.Router.Reg("Api", New cApiController)
    Call Server.Router.Add("/api/users", "Api@Users", OnlyGet)
    Call Server.Router.Add("/api/data", "Api@Data", OnlyGet)

    ' Business controllers
    Call Server.Router.Reg("User", New cUserController)
    Call Server.Router.Add("/user/login", "User@Login", OnlyPost)

    ' ========== Start Service ==========
    ' WebRoot for static files, API requests go to routing
    Server.WebRoot("C:\WebRoot").Start 8080

    Debug.Print "Service started successfully"
    Debug.Print "  Frontend: http://localhost:8080/"
    Debug.Print "  API:      http://localhost:8080/api/users"
End Sub

Single Page Application (SPA) Support

For React/Vue/Angular SPAs, configure all routes to return index.html:

vb
' cSpaController.cls
Public Sub Index(ctx As cHttpServerContext)
    ' Return index.html for frontend routing
    ctx.Response.File "/index.html"
End Sub

' Register routes
Private Sub Form_Load()
    Set Server = New cHttpServer

    ' API routes
    Call Server.Router.Reg("Api", New cApiController)
    Call Server.Router.Add("/api/*", "Api@Handle")

    ' SPA routes: All non-API requests return index.html
    Call Server.Router.Reg("Spa", New cSpaController)
    Call Server.Router.Add("/*", "Spa@Index")

    Server.WebRoot("C:\WebRoot").Start 8080
End Sub

File Upload Directory

vb
' Upload files to static directory
Public Sub Upload(ctx As cHttpServerContext)
    ' Save uploaded file
    Dim savePath As String
    savePath = ctx.Server.WebRoot & "\upload\" & filename

    Call SaveUploadFile(ctx.Request.RawBodyBin, savePath)

    ' Return accessible URL
    Dim result As New Dictionary
    result("url") = "/upload/" & filename
    ctx.Response.Json result
End Sub

Cache Control

Built-in Lazy Cache + ETag + 304

HttpServer has a three-layer cache mechanism that automatically senses file updates, no manual intervention needed:

Request /css/style.css


1. Calculate ETag (file modification time + size, only FSO metadata, no content read)

   ├─> Browser sends If-None-Match and matches → 304 Not Modified ✅
   │     (Zero transfer, browser uses local cache)

   ├─> Cache hit and ETag unchanged → Memory output (skip disk I/O) ✅

   ├─> Cache hit but ETag changed → Re-read + update cache 🔄

   └─> No cache → Disk read + lazy cache (only add if size ≤ limit) 📥

Lazy Cache Rules:

  • No files are cached by default
  • On first access, if file size ≤ MaxCacheFileSize, it's automatically added to memory cache
  • Files exceeding size limit (e.g., videos, large images) are always read from disk, but still enjoy ETag/304 negotiated cache
  • When files are updated on disk, ETag auto-changes and old cache auto-invalidates and re-reads

Cache Configuration

vb
Private Sub Form_Load()
    Set Server = New cHttpServer

    ' Cache configuration (can be modified before Start or at runtime)
    Server.MaxCacheFileSize = 1048576  ' Single file cache limit: 1MB (default)
    Server.CacheTTLSeconds = 300       ' Cache TTL: 5 minutes (default)

    ' Set to 0 to disable content cache (keep ETag/304 negotiated cache only)
    Server.MaxCacheFileSize = 0

    Server.WebRoot("C:\WebRoot").Start 8080
End Sub
ConfigurationDefaultDescription
MaxCacheFileSize1048576 (1MB)Single file cache limit in bytes, 0=disable content cache
CacheTTLSeconds300 (5min)Cache TTL in seconds, check ETag after expiry to decide refresh, 0=never expire

Manual Cache Refresh

After deploying new files, you can manually refresh directory structure cache and content cache:

vb
' Refresh directory structure cache (call after adding/deleting files)
' Does not affect content cache, content cache senses file updates via ETag
Server.RefreshCache

' Clear all file content cache (force re-read from disk)
Server.ClearFileCache

Best Practice: In most scenarios, only RefreshCache is needed, content cache senses file updates via ETag automatically. Use ClearFileCache only when you need to force release memory.

Custom Middleware Cache Headers

vb
' cCacheMiddleware.cls
Public Sub Entry(ctx As cHttpServerContext)
    ' Add browser cache headers for static files
    If IsStaticFile(ctx.Request.PathInfo) Then
        ' Cache for 1 hour
        ctx.Response.Header("Cache-Control") = "public, max-age=3600"
    End If
End Sub

Private Function IsStaticFile(path As String) As Boolean
    Dim ext As String
    ext = LCase(Mid(path, InStrRev(path, ".")))

    IsStaticFile = (ext = ".css" Or ext = ".js" Or ext = ".png" Or _
                    ext = ".jpg" Or ext = ".gif" Or ext = ".ico")
End Function

Last Updated: 2026-06-22

VB6 and LOGO copyright of Microsoft Corporation