Directory Listings
Directory listings provide an Apache-style file browser for directories that don't have an index.html file. This feature is disabled by default and can be enabled on a per-repository basis.
Overview
When enabled, the directory listing feature displays a beautifully formatted HTML page showing all files and folders in a directory, similar to Apache's directory indexing but with a modern, responsive design.
When Are Directory Listings Shown?
Directory listings are displayed when all of these conditions are met:
- ✅
directory_index: trueis set in the repository's.pagesfile - ✅ A directory URL is requested (no file extension in path)
- ✅ No
index.htmlfile exists in that directory - ✅ The directory exists and contains files
If any condition is not met, a 404 error is returned instead.
Features
Visual Design
- Modern Interface: Clean, professional design with gradient header
- Responsive Layout: Works perfectly on desktop, tablet, and mobile devices
- Color-Coded Icons:
- 📁 Yellow folder icons for directories
- 📄 Blue file icons for files
- ⬅️ Gray back arrow for parent directory
- Hover Effects: Interactive row highlighting on hover
- Typography: Clean, readable system fonts
Functionality
- File Information: Shows file name, type (File/Directory), and size
- Parent Navigation: "Parent Directory" link to go up one level (when not at root)
- Clickable Entries: All files and folders are clickable links
- Human-Readable Sizes: File sizes shown as B, KB, MB, or GB
- Breadcrumb Path: Shows current location with username/repository
- Sorted Entries: Files and folders listed in API-provided order
Performance
- Cached: Directory listings are cached for 60 seconds
- Fast API Calls: Uses Forgejo's directory contents API
- Lightweight: No external dependencies, pure Go implementation
Configuration
Enable Directory Listings
Add directory_index: true to your repository's .pages file:
enabled: true
directory_index: true
Disable Directory Listings (Default)
Either omit the field or set it to false:
enabled: true
directory_index: false
Or simply don't include it (defaults to false):
enabled: true
Usage Examples
Example 1: Documentation Repository
Use Case: Documentation with nested folders
.pages file:
enabled: true
directory_index: true
Repository structure:
public/
├── index.html (homepage)
├── api/
│ ├── v1/
│ │ └── reference.html
│ └── v2/
│ └── reference.html
└── guides/
├── installation.html
└── configuration.html
URLs:
https://username.pages.domain.com/docs/→ shows index.htmlhttps://username.pages.domain.com/docs/api/→ shows directory listing (no index.html)https://username.pages.domain.com/docs/api/v1/→ shows directory listinghttps://username.pages.domain.com/docs/guides/→ shows directory listing
Example 2: File Archive
Use Case: Public file sharing/downloads
.pages file:
enabled: true
directory_index: true
Repository structure:
public/
├── downloads/
│ ├── app-v1.0.zip
│ ├── app-v1.1.zip
│ └── app-v2.0.zip
└── assets/
├── logo.png
└── banner.jpg
URLs:
https://username.pages.domain.com/files/downloads/→ shows list of all zip fileshttps://username.pages.domain.com/files/assets/→ shows list of image files
Example 3: Mixed Content
Use Case: Some directories with index, some without
.pages file:
enabled: true
directory_index: true
Repository structure:
public/
├── index.html (has index)
├── blog/
│ ├── index.html (has index)
│ └── 2024/ (no index - will show listing)
│ ├── post1.html
│ └── post2.html
└── resources/ (no index - will show listing)
├── document.pdf
└── spreadsheet.xlsx
URLs:
https://username.pages.domain.com/site/→ shows index.htmlhttps://username.pages.domain.com/site/blog/→ shows index.htmlhttps://username.pages.domain.com/site/blog/2024/→ shows directory listinghttps://username.pages.domain.com/site/resources/→ shows directory listing
Directory Listing Display
What Users See
When a directory listing is shown, users see a page with:
Header:
- Title: "Index of /path/to/directory"
- Breadcrumb: "username/repository"
- Gradient purple background
Table:
- Name column: File/folder name with icon
- Type column: "File" or "Directory"
- Size column: Human-readable file size (or "-" for directories)
Footer:
- "Powered by Bovine Pages Server"
Sample Directory Listing
┌─────────────────────────────────────────────────┐
│ Index of /downloads │
│ username/repository │
└─────────────────────────────────────────────────┘
┌─────────────────────────┬──────────┬───────────┐
│ Name │ Type │ Size │
├─────────────────────────┼──────────┼───────────┤
│ ⬅️ Parent Directory │ - │ - │
│ 📁 archives │ Directory│ - │
│ 📄 app-v1.0.zip │ File │ 2.3 MB │
│ 📄 app-v2.0.zip │ File │ 3.1 MB │
│ 📄 readme.txt │ File │ 1.2 KB │
└─────────────────────────┴──────────┴───────────┘
Powered by Bovine Pages Server
Security Considerations
What Directory Listings Expose
When directory listings are enabled, all files and folders in directories without index.html become visible to anyone who can access your site.
⚠️ Important Security Notes:
- Don't Store Secrets: Never put sensitive files in public directories with directory listings enabled
- Check File Permissions: Remember that if someone can see the filename, they can access the file
- Consider Privacy: File names themselves may reveal sensitive information
- Use Password Protection: Combine with password protection for sensitive archives
Recommended Practices
✅ Good Use Cases:
- Public documentation repositories
- Open source project downloads
- Public file archives
- Resource libraries
- Asset directories for websites
❌ Bad Use Cases:
- Private documents repository
- Directories with sensitive filenames
- Backup directories
- Configuration files
- Internal-only resources
Combine with Password Protection
For semi-private file sharing, combine directory listings with password protection:
enabled: true
directory_index: true
password: your-sha256-hash-here
This allows authorized users to browse files while keeping them private from the public.
Limitations
Profile Sites (.profile Repository)
⚠️ Directory listings do not work for profile site directories.
Why This Limitation Exists:
The URL structure for pages domain requests uses the first path segment to determine whether you're accessing:
- A repository (e.g.,
https://username.pages.domain.com/myrepo) - A file in the profile site (e.g.,
https://username.pages.domain.com/about.html)
The system uses file extensions to distinguish between these cases:
- Has extension (
.html,.css, etc.) → Profile site file - No extension → Repository name
Because directories don't have file extensions, a URL like https://username.pages.domain.com/downloads/ is interpreted as a repository named "downloads", not as a directory in the profile site.
Workaround:
If you need directory listings for your profile site, use a regular repository instead:
- Create a repository (e.g.,
mysite) - Enable directory listings in that repository's
.pagesfile - Access directories via:
https://username.pages.domain.com/mysite/downloads/
Custom Domains Work Normally:
If you use a custom domain for your profile site, directory listings work as expected because the URL structure is different:
- Custom domain:
https://custom.domain.com/downloads/✅ Works - Pages domain:
https://username.pages.domain.com/downloads/❌ Interpreted as repository
Technical Details
How It Works
- Request Arrives: User navigates to a directory URL (e.g.,
/downloads/) - Try index.html: Plugin first tries to serve
index.htmlfrom that directory - Check Configuration: If no
index.html, checks.pagesfile fordirectory_index: true - Fetch Directory: Calls Forgejo API to list directory contents
- Generate HTML: Creates beautiful HTML listing page
- Serve & Cache: Serves the page and caches it for 60 seconds
API Calls
The feature uses Forgejo's repository contents API:
GET /api/v1/repos/{owner}/{repo}/contents/{path}
This returns a JSON array of files and directories with metadata.
Performance Impact
- Additional API Call: One extra API call when directory listing is triggered
- Minimal Overhead: Listing generation is very fast (< 1ms for typical directories)
- Caching: Results cached for 60 seconds to reduce repeated API calls
- No Impact on Files: Regular file serving is unaffected
Mobile Responsiveness
On mobile devices (< 768px width):
- Type and Size columns are hidden
- Table shows only the Name column with icons
- Maintains full functionality with cleaner layout
- Touch-friendly link targets
Troubleshooting
Directory Listing Not Showing
Problem: Expected directory listing but seeing 404 error
Checklist:
- ✅ Is
directory_index: truein your.pagesfile? - ✅ Are you accessing a directory (URL ends with
/or no extension)? - ✅ Does the directory NOT have an
index.htmlfile? - ✅ Does the directory exist and contain files?
- ✅ Is the
.pagesfile properly formatted YAML?
Test:
# Check .pages file
curl https://git.example.com/api/v1/repos/username/repo/contents/.pages
# Check directory exists
curl https://git.example.com/api/v1/repos/username/repo/contents/public/yourdir
Showing Empty Listing
Problem: Directory listing shows but has no files
Possible Causes:
- Directory is truly empty
- All files start with
.(hidden files) - Forgejo API may not return them - Directory only contains subdirectories (check if you're in the right place)
index.html Takes Precedence
Problem: Directory listing doesn't show even though enabled
Explanation: This is expected behavior! If an index.html file exists, it takes precedence over directory listings. This is by design.
Solution: If you want directory listing for a specific directory, remove or rename its index.html file.
Styling Issues
Problem: Directory listing appears but styling is broken
Possible Causes:
- Browser caching old version - Clear cache and reload
- Content-Security-Policy blocking inline styles - Check browser console
- Very old browser - Requires modern browser with CSS Grid support
Comparison with Other Solutions
vs. Apache Directory Index
Similarities:
- Shows file and folder listings
- Navigable directory structure
- File size display
Advantages over Apache:
- ✅ Modern, beautiful design
- ✅ Responsive mobile layout
- ✅ Color-coded icons
- ✅ Per-repository configuration
- ✅ Integrated with git repository structure
Differences:
- ❌ No column sorting (yet)
- ❌ No date modified column
- ✅ Opt-in (disabled by default)
vs. index.html with File List
Why Use Directory Listings Instead:
- ✅ Automatic - no manual HTML updates
- ✅ Always current - reflects repository state
- ✅ Less maintenance - no need to update file list
- ✅ Works for any directory depth
When to Use Manual index.html:
- Custom styling and branding
- Descriptions and metadata for files
- Custom grouping and categorization
- SEO optimization
Examples in the Wild
Documentation Repository
A documentation site with directory listings for API versions:
https://username.pages.domain.com/docs/
├── index.html (main docs page)
├── v1/ (directory listing)
│ ├── endpoints.html
│ └── authentication.html
└── v2/ (directory listing)
├── endpoints.html
└── authentication.html
Download Archive
A software project with versioned downloads:
https://username.pages.domain.com/downloads/
└── releases/ (directory listing)
├── v1.0.0/ (directory listing)
│ ├── app-linux.tar.gz
│ └── app-windows.zip
└── v2.0.0/ (directory listing)
├── app-linux.tar.gz
└── app-windows.zip
Future Enhancements
Potential features for future versions:
- 📊 Column sorting (by name, size, type)
- 📅 Last modified date display
- 🔍 Search/filter functionality
- 📁 Custom icons by file extension
- 🎨 Themeable design
- 📝 Custom header/footer content
- 📦 Bulk download (zip)
Related Features
- Password Protection - Combine with directory listings for private file sharing
- Custom Domains - Use directory listings on your custom domain
- Configuration Guide - General .pages file configuration
Support
- Issues: Report a bug
- Documentation: Main Wiki