Static Files
Overview Questions
- ASP.NET Core phục vụ static files như thế nào?
wwwrootfolder là gì và cấu hình ra sao?- Làm sao để enable directory browsing?
- File providers là gì và khi nào cần custom file provider?
- Cache headers cho static files được cấu hình ra sao?
Serving Static Files
Default Configuration
// Program.cs
var app = builder.Build();
// Enable static files middleware
app.UseStaticFiles();
// Static files được phục vụ từ wwwroot folder
// http://localhost/images/logo.png -> wwwroot/images/logo.png
Custom Static File Location
// Serve từ folder khác
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
RequestPath = "/static"
});
// http://localhost/static/logo.png -> MyStaticFiles/logo.png
wwwroot Structure
Project/
├── wwwroot/
│ ├── css/
│ │ └── site.css
│ ├── js/
│ │ └── site.js
│ ├── images/
│ │ └── logo.png
│ └── lib/
│ └── bootstrap/
├── Program.cs
└── appsettings.json
HTML Reference
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/css/site.css" />
</head>
<body>
<img src="/images/logo.png" alt="Logo" />
<script src="/js/site.js"></script>
</body>
</html>
Directory Browsing
Enable Directory Browsing
var app = builder.Build();
// Enable directory browsing
app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "wwwroot")),
RequestPath = "/files"
});
// Hoặc cho toàn bộ static files
app.UseStaticFiles();
app.UseDirectoryBrowser();
Security Warning
⚠️ Chỉ enable directory browsing cho development hoặc khi cần thiết. Production nên disable để tránh expose file structure.
File Providers
Physical File Provider
// Default - sử dụng PhysicalFileProvider
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "wwwroot"))
});
Embedded File Provider
// Serve files từ embedded resources
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new EmbeddedFileProvider(
typeof(Program).Assembly,
"MyApp.EmbeddedFiles")
});
Composite File Provider
// Kết hợp nhiều file providers
var compositeProvider = new CompositeFileProvider(
new PhysicalFileProvider(Path.Combine(builder.Environment.ContentRootPath, "wwwroot")),
new EmbeddedFileProvider(typeof(Program).Assembly, "MyApp.EmbeddedFiles")
);
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = compositeProvider
});
Content Types
Default Content Types
// ASP.NET Core tự động detect content type từ file extension
// .css -> text/css
// .js -> application/javascript
// .png -> image/png
// .html -> text/html
Custom Content Types
var provider = new FileExtensionContentTypeProvider();
// Add custom mapping
provider.Mappings[".webp"] = "image/webp";
provider.Mappings[".custom"] = "application/x-custom";
// Remove mapping
provider.Mappings.Remove(".rtf");
app.UseStaticFiles(new StaticFileOptions
{
ContentTypeProvider = provider
});
Cache Headers
Default Behavior
// Static files middleware tự động thêm Last-Modified header
// Browser sẽ cache và send If-Modified-Since request
// Server trả về 304 Not Modified nếu file không thay đổi
Custom Cache Headers
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// Cache 1 năm cho versioned files
const int durationInSeconds = 60 * 60 * 24 * 365;
ctx.Context.Response.Headers["Cache-Control"] =
$"public, max-age={durationInSeconds}";
}
});
Response Caching Middleware
// Add response caching
app.UseResponseCaching();
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers["Cache-Control"] = "public, max-age=3600";
ctx.Context.Response.Headers["Vary"] = "Accept-Encoding";
}
});
Security Considerations
Block Sensitive Files
// Block access to sensitive file types
app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = false, // Default: false
OnPrepareResponse = ctx =>
{
var path = ctx.Context.Request.Path.Value;
if (path.EndsWith(".json") || path.EndsWith(".config"))
{
ctx.Context.Response.StatusCode = 403;
}
}
});
Best Practices
┌─────────────────────────────────────────────────────────────────┐
│ STATIC FILE SECURITY │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ✅ DO: │
│ - Chỉ serve từ wwwroot hoặc folder chỉ định │
│ - Disable directory browsing trong production │
│ - Set appropriate cache headers │
│ - Use CDN for production static files │
│ │
│ ❌ DON'T: │
│ - Serve từ folders chứa sensitive data │
│ - Enable directory browsing không cần thiết │
│ - Serve .config, .json, .env files │
│ - Forget to validate file uploads │
│ │
└─────────────────────────────────────────────────────────────────┘
Default Files
Serve Default File
// Serve default.html khi access root
app.UseDefaultFiles(new DefaultFilesOptions
{
DefaultFileNames = new List<string> { "index.html", "default.html" }
});
// Phải đặt TRƯỚC UseStaticFiles
app.UseStaticFiles();
// http://localhost/ -> wwwroot/index.html
UseFileServer (Combined)
// Kết hợp UseDefaultFiles + UseStaticFiles + UseDirectoryBrowser
app.UseFileServer();
// Hoặc với custom options
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(builder.Environment.ContentRootPath, "wwwroot")),
EnableDirectoryBrowsing = false
});